2016-05-03 18 views
1

verallgemeinern Der Versuch, Ich versuche, eine generali Klasse in Swift, Xcode 7.3 (so Swift 2.2) zu erstellen, aber ich kann es nicht vorbei an den Compiler scheinen zu bekommen:in schnellen

protocol Struct1Protocol { 
} 

struct Struct1 { 
    var name1: String 
} 

protocol Struct2Protocol { 
} 

struct Struct2: Struct2Protocol { 
    var name2: String 
} 

class StructDependent<T> { 
    func setupFrom<T:Struct1Protocol>(value: T) { 
     print("we want to setup StructDependent with struct 1") 
    } 

    func setupFrom<T:Struct2Protocol>(value: T) { 
     print("we want to setup StructDependent with struct 2") 
    } 
} 

class A<T> { 
    func test(value: T) { 
     let structInstance = StructDependent<T>() 
     // this gives a compiler error: 
     // Cannot invoke 'setupFrom' with an argument list of type '(T)' 
     structInstance.setupFrom(value) 
    } 
} 

Die Idee ist, habe eine StructDependent, die aus verschiedenen Strukturen aufgebaut werden kann. Klasse A sollte in der Lage sein, setupFrom aufzurufen, wenn die Klasse mit einer kompatiblen Struktur instanziiert wurde. Wie so:

let a = A<Struct1>() 
let v = Struct1(name1: "") 
a.test(v) 

Wie würde ich darüber gehen? Ich bin hier ein bisschen blockiert, also sind alle Ideen willkommen.

+0

Ich weiß nicht, wie das da reingekommen ist, muss versehentlich in meinem Copy Paste Buffer gewesen sein. Ich korrigierte den Beispielcode zu dem, was ich in Xcode habe. –

+0

Okay, aber es ist immer noch das gleiche Problem. Es gibt nichts über "value: T", um dem Compiler mitzuteilen, dass "T" Struct1Protocol oder Struct2Protocol annimmt. Wie kann es dann verwendet werden, wo ein Struct1Protocol oder Struct2Protocol benötigt wird? – matt

+0

Warum haben Sie nicht Struct1 und Struct2 adoptieren _the same_ Protokoll? Auf diese Weise können Sie auch "value: T" eingeben. – matt

Antwort

3

Es scheint mir, dass Sie diese Art und Weise überdenken. Ich würde einen viel einfacheren Blick auf den Fall werfen und es ganz ohne Generika machen; stattdessen wir nur ein Protokoll als eine Art übergeordneter Typ der beiden Strukturen verwenden (so wie wir eine übergeordnete Klasse, wenn die structs Klassen waren verwenden würde):

protocol StructProtocol { 
    var name : String {get set} 
    func setup() 
} 

struct Struct1 : StructProtocol{ 
    var name: String 
    func setup() {} 
} 

struct Struct2 : StructProtocol { 
    var name: String 
    func setup() {} 
} 

class StructDependent { 
    func setup(s:StructProtocol) { 
     s.setup() // or not, I don't care... 
     // or you could just switch on the type, e.g.: 
     switch s { 
     case is Struct1: // ... 
     case is Struct2: // ... 
     default: break 
     } 
    } 
} 

class A { 
    func test(value: StructProtocol) { 
     let structInstance = StructDependent() 
     structInstance.setup(value) 
    } 
} 

Wenn StructDependent selbst wirklich verschiedene Dinge tun muss, je Auf was setup mit aufgerufen wird, kann es den tatsächlichen Typ einschalten. Aber es wäre besser der erste Weg, wo wir einfach etwas nennen, das sowohl Struct1 als auch Struct2 auf ihre eigene Art zu tun weiß.

+0

Sieht aus, als hätte ich das wirklich übertrieben. Vielen Dank!! –

+0

Ich überarbeite, so dass Struct1 und Struct2 beide die Eigenschaft 'name' haben, falls Sie daran gedacht haben, das zu verwenden. Wenn dies vom Protokoll auferlegt wird, lässt der Compiler sie glücklicherweise von beiden unter der Protokollrubrik abholen. – matt

1

Sie müssen eine type constraint auf Ihren generischen Typ setzen. Sie können ein allumfassendes Protokoll für diese Einschränkung verwenden.

protocol StructProtocol { 
} 

protocol Struct1Protocol: StructProtocol { 
} 

struct Struct1: Struct1Protocol { 
var name1: String 
} 

protocol Struct2Protocol: StructProtocol { 
} 

struct Struct2: Struct2Protocol { 
var name2: String 
} 



class StructDependent<T> { 
func setupFrom<T:Struct1Protocol>(value: T) { 
    print("we want to setup StructDependent with struct 1") 
} 

func setupFrom<T:Struct2Protocol>(value: T) { 
    print("we want to setup StructDependent with struct 2") 
} 
} 

class A<T: Struct1Protocol> { 
func test(value: T) { 
    let structInstance = StructDependent<T>() 
    // this gives a compiler error: 
    // Cannot invoke 'setupFrom' with an argument list of type '(T)' 
    structInstance.setupFrom(value) 
} 
} 

let a = A<Struct1>() 
+0

Ja, das war auch mein erster Vorschlag (in meinen Kommentaren zu der ursprünglichen Frage). Aber wenn wir Ockhams Rasiermesser auf Ihre Antwort anwenden, bekommen wir genau meine Antwort; Sie sind im Grunde gleich! Cool. – matt

+0

Ihre Antwort hat mir auch geholfen, R.P. !! Es ist schade, dass ich nur einen auf StackOverflow als korrekt akzeptieren kann. –

+0

Ich bin immer noch nicht da. Ich schaffte es, Dinge zu kompilieren, und ich dachte, dass ich ziemlich gut unterwegs war, aber dann funktionierte es zur Laufzeit nicht. Ich habe meinen Code in einem Mini-Projekt getrennt. Es hat alles damit zu tun, eine Menge allgemeiner TableView-Codeklassen zu erstellen. Also im Beispiel, dass Sie hier herunterladen können https://dl.dropboxusercontent.com/u/165243/tableviewgeneralization.zip gibt es ein konkreteres Beispiel für das, was ich tun möchte. Jede Hilfe immer noch geschätzt. –