2016-05-21 7 views
6

Ich habe ein Protokoll, Address, die von einem anderen Protokoll erbt, Validator und Address erfüllt die Validator Anforderung in der Verlängerung.kann nicht in Swift in einem anderen Protokoll Protokoll als associatedtype verwendet

Es ist ein weiteres Protokoll, FromRepresentable, die eine associatedType (ValueWrapper) Anforderung hat die Validator sein sollte.

Wenn ich jetzt versuche, Address als associatedType zu verwenden, dann kompiliert es nicht. Er sagt,

Inferred Typ 'Adresse' (durch entsprechende Anforderung 'valueForDetail') ist ungültig: nicht entspricht 'Validator'.

Ist diese Verwendung illegal? Sollten wir nicht in der Lage sein Address anstelle von Validator zu verwenden, wie alle Addresses sind Validator.

Unten ist das Stück Code, den ich versuche.

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 
     } 
    } 
} 

Update: Filed ein bug.

Antwort

4

das Problem, das David has already eluded to, dass, sobald Sie ist Beschränken Sie die Protokoll associatedtype auf ein bestimmtes Protokoll, müssen Sie einen konkreten Typ verwenden, um diese Anforderung
zu erfüllen.

Dies liegt daran, protocols don't conform to themselves - also was bedeutet, dass Sie nicht verwenden können Address dem Protokoll des zugehörigen Typ Anforderung eines Typs zu erfüllen, die zu Validator entspricht, wie Address ist nicht ein Typ, der zu Validator entspricht.

Wie ich in my answer here zeigen, betrachtet das Gegenbeispiel:

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 
} 

Die einfachste Lösung, um die Validator Protokoll Einschränkung auf Ihrem ValueWrapper zugehörigen Typ zu Graben wäre, so dass Sie eine abstrakte Art in dem Verfahren verwenden, Streit.

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

enum AddressFrom : Int, FormRepresentable { 

    // ... 

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

Wenn Sie die Typeinschränkung verbunden sind, und jeder AddressFrom Instanz erwartet nur eine einzige konkrete Umsetzung von Address als Eingabe - Sie Generika, um für Ihre AddressFrom verwenden könnte mit einem bestimmten konkreten Adresstyp initialisiert werden, der in Ihrer Methode verwendet werden soll.

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 

Wenn Sie jedoch sowohl die zugehörige Typeinschränkung und jeder AddressFrom erfordern Instanz muss in der Lage sein, eine Eingabe von jeder zu handhaben Art von Address - Sie werden eine Art löschen verwenden, um eine beliebige Address in einem konkreten Typ zu wickeln.

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

Okay, ich bekomme es, aber gibt es einen Grund, warum wir concreteType für einen associatedType mit einer Protokollbeschränkung verwenden müssen? Compiler gibt keinen Fehler aus, wenn wir Adresse als Argument für eine Funktion verwenden, die Validator erwartet. –

+0

Ich verstehe es nicht, warum müssen wir konkreten Typ verwenden, wenn AssociatedType Protokolleinschränkung hat. Hat es etwas mit Invarianzverhalten oder Speicherzuordnung zu tun? Haben Sie einen Hinweis, wo ich mehr Informationen dazu finden kann? –

+0

@VishalSingh Ich kann leider keinen besseren Grund als "weil das so ist" anbieten - ich kann mir keinen Grund vorstellen, warum es nicht möglich sein sollte, da die uneingeschränkte Version gut funktioniert . Ich kann auch nichts über die evolution Mailingliste oder den Bugtracker finden. Es könnte sich lohnen, einen [Fehlerbericht] (https://bugs.swift.org/secure/Dashboard.jspa) einzureichen und zu sehen, was sie dazu sagen. – Hamish

0

Sie eine Reihe von Fragen haben:

Zu allererst Sie erklären nicht wirklich, dass die Adresse implementiert Validator

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

Und Sie deklarieren den zugehörigen Typ für ValueWrapper nicht:

typealias ValueWrapper = ShippingAddress 

Und Sie scheinen tatsächlich AddressFrom.valueForDetail werden wollen, um eine ShippingAddress zu nehmen: Insgesamt

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

, es sieht aus wie:

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 Ich habe diese Frage gepostet, während ich noch versuchte, es zum Laufen zu bringen. Ich habe das Code-Snippet bearbeitet. Adresse erbt tatsächlich vom Validator. In Bezug auf Typalias, ich denke, es sollte in der Lage sein, ValueWrapper von valueForDetail (_: Address) -Funktion abzuleiten. Ich will nicht, dass es konkret ist. –

+0

Beachten Sie, dass Sie den "associatedtype" des Protokolls (über die "typealias") in Ihrer Implementierung nicht explizit definieren müssen, Swift kann dies aus dem Argumenttyp method ableiten. – Hamish

Verwandte Themen