2016-05-21 20 views
6

Tôi có giao thức, Address, được kế thừa từ giao thức khác, ValidatorAddress đáp ứng yêu cầu Validator trong tiện ích.Không thể sử dụng giao thức làm loại liên kết trong giao thức khác trong Swift

Có một giao thức khác, FromRepresentable, có yêu cầu associatedType (ValueWrapper) cần phải là Validator.

Bây giờ, nếu tôi cố gắng sử dụng Address làm associatedType, thì nó sẽ không biên dịch. Nó nói,

Loại được hỏi 'Địa chỉ' (theo yêu cầu 'giá trịForDetail') là không hợp lệ: không phù hợp với 'Trình xác thực'.

Việc sử dụng này có hợp pháp không? Chúng ta không thể sử dụng Address thay cho Validator, vì tất cả AddressesValidator.

Dưới đây là đoạn mã tôi đang cố gắng.

enum ValidationResult { 
    case Success 
    case Failure(String) 
} 

protocol Validator { 
    func validate() -> ValidationResult 
} 

//Address inherits Validator 
protocol Address: Validator { 
    var addressLine1: String {get set} 
    var city: String {get set} 
    var country: String {get set} 
} 

////Fulfill Validator protocol requirements in extension 
extension Address { 
    func validate() -> ValidationResult { 
     if addressLine1.isEmpty { 
      return .Failure("Address can not be empty") 
     } 
     return .Success 
    } 
} 

protocol FormRepresentable { 
    associatedtype ValueWrapper: Validator 
    func valueForDetail(valueWrapper: ValueWrapper) -> String 
} 


// Shipping Address conforming to Address protocol. 
// It should also implicitly conform to Validator since 
// Address inherits from Validator? 
struct ShippingAddress: Address { 
    var addressLine1 = "CA" 
    var city = "HYD" 
    var country = "India" 
} 


// While compiling, it says: 
// Inferred type 'Address' (by matching requirement 'valueForDetail') is invalid: does not conform 
// to 'Validator'. 
// But Address confroms to Validator. 
enum AddressFrom: Int, FormRepresentable { 
    case Address1 
    case City 
    case Country 

    func valueForDetail(valueWrapper: Address) -> String { 
     switch self { 
     case .Address1: 
      return valueWrapper.addressLine1 
     case .City: 
      return valueWrapper.city 
     case .Country: 
      return valueWrapper.country 
     } 
    } 
} 

Cập nhật: Filed một bug.

Trả lời

4

Các vấn đề, mà David has already eluded to, là một khi bạn hạn chế giao thức associatedtype của giao thức cụ thể, bạn phải sử dụng loại bê tông để đáp ứng yêu cầu
đó.

Điều này là do protocols don't conform to themselves - do đó có nghĩa là bạn không thể sử dụng Address để đáp ứng yêu cầu loại liên quan của Nghị định thư của một loại mà phù hợp với Validator, như Addresskhông một loại mà phù hợp với Validator.

Như tôi đã chứng minh in my answer here, hãy xem xét các phản ví dụ về:

protocol Validator { 
    init() 
} 
protocol Address : Validator {} 

protocol FormRepresentable { 
    associatedtype ValueWrapper: Validator 
} 

extension FormRepresentable { 
    static func foo() { 
     // if ValueWrapper were allowed to be an Address or Validator, 
     // what instance should we be constructing here? 
     // we cannot create an instance of a protocol. 
     print(ValueWrapper.init()) 
    } 
} 

// therefore, we cannot say: 
enum AddressFrom : FormRepresentable { 
    typealias ValueWrapper = Address 
} 

Giải pháp đơn giản nhất sẽ phải bỏ đi điều chế Validator Nghị định thư về ValueWrapper loại liên quan đến bạn, cho phép bạn sử dụng một loại trừu tượng trong phương pháp tranh luận.

protocol FormRepresentable { 
    associatedtype ValueWrapper 
    func valueForDetail(valueWrapper: ValueWrapper) -> String 
} 

enum AddressFrom : Int, FormRepresentable { 

    // ... 

    func valueForDetail(valueWrapper: Address) -> String { 
     // ... 
    } 
} 

Nếu bạn cần các loại hạn chế liên quan, và mỗi AddressFrom dụ chỉ hy vọng một thực hiện cụ thể duy nhất của Address như một đầu vào - bạn có thể sử dụng Generics để cho bạn AddressFrom để được khởi tạo với một loại địa chỉ cụ thể được sử dụng trong phương thức của bạn.

protocol FormRepresentable { 
    associatedtype ValueWrapper : Validator 
    func valueForDetail(valueWrapper: ValueWrapper) -> String 
} 

enum AddressFrom<T : Address> : Int, FormRepresentable { 

    // ... 

    func valueForDetail(valueWrapper: T) -> String { 
     // ... 
    } 
} 

// replace ShippingAddress with whatever concrete type you want AddressFrom to use 
let addressFrom = AddressFrom<ShippingAddress>.Address1 

Tuy nhiên, nếu bạn yêu cầu cả hai loại hạn chế liên quan đến mỗi AddressFrom dụ phải có khả năng xử lý một đầu vào của bất kỳ loại Address - bạn sẽ sử dụng loại tẩy xoá để bọc Address tùy ý theo loại bê tông.

protocol FormRepresentable { 
    associatedtype ValueWrapper : Validator 
    func valueForDetail(valueWrapper: ValueWrapper) -> String 
} 

struct AnyAddress : Address { 

    private var _base: Address 

    var addressLine1: String { 
     get {return _base.addressLine1} 
     set {_base.addressLine1 = newValue} 
    } 
    var country: String { 
     get {return _base.country} 
     set {_base.country = newValue} 
    } 
    var city: String { 
     get {return _base.city} 
     set {_base.city = newValue} 
    } 

    init(_ base: Address) { 
     _base = base 
    } 
} 

enum AddressFrom : Int, FormRepresentable { 

    // ... 

    func valueForDetail(valueWrapper: AnyAddress) -> String { 
     // ... 
    } 
} 

let addressFrom = AddressFrom.Address1 

let address = ShippingAddress(addressLine1: "", city: "", country: "") 

addressFrom.valueForDetail(AnyAddress(address)) 
+0

Được rồi, tôi hiểu rồi, nhưng có lý do nào để chúng ta phải sử dụng concreteType cho một kiểu liên kết với ràng buộc giao thức không? Trình biên dịch không phát ra bất kỳ lỗi nào nếu chúng ta sử dụng Địa chỉ làm đối số cho một hàm mong đợi Validator. –

+0

Tôi không hiểu tại sao chúng ta phải sử dụng loại cụ thể nếu liên kếtType có ràng buộc giao thức. Liệu nó có liên quan đến hành vi bất biến hay phân bổ bộ nhớ? Bạn có bất kỳ tài liệu tham khảo mà tôi có thể biết thêm thông tin về điều này. –

+0

@VishalSingh Tôi sợ tôi không thể đưa ra bất kỳ lý do nào tốt hơn "bởi vì đó chỉ là cách nó" - tôi chắc chắn không thể nghĩ ra lý do tại sao nó không thể được, nhìn thấy như phiên bản không bị giới hạn hoạt động tốt . Tôi cũng không thể tìm thấy bất cứ điều gì trên danh sách gửi thư tiến hóa nhanh chóng hoặc theo dõi lỗi về điều này. Nó cũng có thể là giá trị nộp một [báo cáo lỗi] (https://bugs.swift.org/secure/Dashboard.jspa) trên, và nhìn thấy những gì họ nói về nó. – Hamish

0

Bạn đã có một số vấn đề:

Trước hết, bạn không thực sự tuyên bố rằng Địa chỉ thực hiện Validator

//Address inherits Validator 
protocol Address : Validator { 
    var addressLine1: String {get set} 
    var city: String {get set} 
    var country: String {get set} 
} 

Và bạn không khai báo loại kết hợp cho ValueWrapper:

typealias ValueWrapper = ShippingAddress 

Và bạn dường như thực sự được muốn AddressFrom.valueForDetail để có một ShippingAddress:

func valueForDetail(valueWrapper: ShippingAddress) -> String { 
    switch self { 
    case .Address1: 
     return valueWrapper.addressLine1 
    case .City: 
     return valueWrapper.city 
    case .Country: 
     return valueWrapper.country 
    } 
} 

Nhìn chung, nó trông giống như:

enum ValidationResult { 
    case Success 
    case Failure(String) 
} 

protocol Validator { 
    func validate() -> ValidationResult 
} 

//Address inherits Validator 
protocol Address : Validator { 
    var addressLine1: String {get set} 
    var city: String {get set} 
    var country: String {get set} 
} 

////Fulfill Validator protocol requirements in extension 
extension Address { 
    func validate() -> ValidationResult { 
     if addressLine1.isEmpty { 
      return .Failure("Address can not be empty") 
     } 
     return .Success 
    } 
} 

protocol FormRepresentable { 
    associatedtype ValueWrapper: Validator 
    func valueForDetail(valueWrapper: ValueWrapper) -> String 
} 


// Shipping Address conforming to Address protocol. 
// It should also implicity conform to Validator since 
// Address inherits from Validator? 
struct ShippingAddress: Address { 
    var addressLine1 = "CA" 
    var city = "HYD" 
    var country = "India" 
} 


// While compiling, it says: 
// Inferred type 'Address' (by matching requirement 'valueForDetail') is invalid: does not conform 
// to 'Validator'. 
// But Address confroms to Validator. 
enum AddressFrom: Int, FormRepresentable { 
    case Address1 
    case City 
    case Country 

    // define associated type for FormRepresentable 
    typealias ValueWrapper = ShippingAddress 
    func valueForDetail(valueWrapper: ShippingAddress) -> String { 
     switch self { 
     case .Address1: 
      return valueWrapper.addressLine1 
     case .City: 
      return valueWrapper.city 
     case .Country: 
      return valueWrapper.country 
     } 
    } 
} 
+0

Hey s orry tôi đăng câu hỏi này trong khi tôi vẫn cố gắng để làm cho nó hoạt động. Tôi đã chỉnh sửa đoạn mã. Địa chỉ thực sự được kế thừa từ Validator. Về typealias, tôi nghĩ rằng nó sẽ có thể suy ra ValueWrapper từ hàm valueForDetail (_: Address). Tôi không muốn nó là concretetype. –

+0

Lưu ý rằng bạn không thực sự phải xác định rõ ràng 'liên kết 'của giao thức (thông qua' typealias') trong quá trình thực hiện của bạn, Swift có thể suy ra nó từ kiểu đối số phương thức. – Hamish

Các vấn đề liên quan