2016-04-24 16 views
6

Ho un protocollo my swift code base Ho protocollo con un tipo associato e due metodi. Entrambi i metodi definiscono diversi vincoli generici per il tipo associato del protocollo. E vorrei rendere la struttura conforme al protocollo due ma con due diversi tipi associati.implementare il protocollo con diverso tipo associato

protocol Convertable { 
    associatedtype TargetType 
    func convert() -> TargetType 
} 

func show<T : Convertable where T.TargetType == String>(toShow : T) { 
    print(toShow.convert()) 
} 
func add<T : Convertable where T.TargetType == Int>(a : T, b : T) -> Int { 
    return a.convert() + b.convert() 
} 

struct MyData { 
    var data : Int 
} 

Come estensione faccio la struct conforme al protocollo in cui la TargetType sarà String per passare al metodo spettacolo:

extension MyData : Convertable { 
    func convert() -> String { return String(self.data) } 
} 

Finora tutto funziona come previsto. Ma ora mi piace anche che la struttura sia conforme al protocollo Convertable quando TargetType è associato a un Int. Quale sembra essere impossibile?

La prima cosa che ho provato è stato quello di aggiungere un secondo definizione del metodo convertito alla estensione:

extension MyData : Convertable { 
    func convert() -> String { return String(self.data) } 
    func convert() -> Int { return data } 
} 

Il compilatore ora lamenta che MyData fa non è più conforme al protocollo. Il secondo era dividere questo in due estensioni e associare esplicitamente il TargetType.

extension MyData : Convertable { 
    typealias TargetType = Int 
    func convert() -> Int { return data } 
} 
extension MyData : Convertable { 
    typealias TargetType = String 
    func convert() -> String { return String(data) } 
} 

Questo ha l'effetto che il compilatore ora si lamenta il il TargetType è stato ridefinito.

Il mio ultimo tentativo è stato quello di definire due protocolli che estendono il protocollo Convertable e vincolare la TargetType e quindi implementare entrambi tramite estensione:

protocol ConvertableString : Convertable { 
    associatedtype TargetType = String 
} 
protocol ConvertableInt : Convertable { 
    associatedtype TargetType = Int 
} 

extension MyData : ConvertableInt { 
    func convert() -> Int { return self.data } 
} 
extension MyData : ConvertableString { 
    func convert() -> String { return String(self.data) } 
} 

Il che rende ora il compilatore felice per le estensioni, ma non più per la chiamata a show perché non sa che può chiamare la funzione con MyData.

C'è qualcosa che ho supervisionato o non è attualmente possibile in swift?

+1

Non è solo al momento non possibile, ma difficilmente sarà mai possibile, a mio parere. Hai dichiarato un protocollo con _one single_tipo associato. Come può essere impostato su due diversi tipi allo stesso tipo ?! – werediver

+1

Beh, non è lo stesso protocollo, dal momento che il protocollo è generico su TargetType ci sono tante varianti del protocollo come per TargetType. L'intera idea di avere tipi associati ai protocolli è che è possibile differenziare il tipo associato. Se si guarda C#, è possibile implementare la stessa interfaccia con tipi diversi legati al parametro generico. – Kolja

+0

Beh, non è C# e non dovresti pensarlo come sarebbe, perché semplicemente non funziona. – werediver

risposta

1

Ho appena finanziato un modo per archiviare questo. Il trucco è quello di aggiungere un altro tipo associato in uno dei sottotipi del protocollo:

protocol ConvertableInt : Convertable { 
    associatedtype TResI 
    typealias TargetType = TResI 
} 

extension MyData : Convertable { 
    typealias TargetType = String 
    func convert() -> String { return String(self.data) } 
} 

extension MyData : ConvertableInt { 
    typealias TResI = Int 
    func convert() -> TResI { return self.data } 
} 

Questo permette anche di sbarazzarsi del secondo sottotipo per la stringa.

Mentre passa il compilatore si blocca completamente in fase di esecuzione!

Il compilatore chiama sempre il metodo definito definito nell'esplicito typealias. In questo caso:

typealias TargetType = String 

che si tradurrà in interpretare l'indirizzo come un intero e vi darà risultati completamente sbagliati. Se lo si definisce, viceversa si bloccherà semplicemente perché tenta di interpretare l'intero come un indirizzo.

Problemi correlati