Entschuldigung für den seltsamen Titel. Hier ist eine Spielzeugskizze meines Codes:
extension UIControl {
func makeHolder() -> ControlHolder {
ControlHolder(control: self)
}
}
struct ControlHolder {
let control : UIControl
init(control: UIControl) {
self.control = control
}
func retrieve() -> UIControl {
return self.control
}
}
Ich gebe zu, dass dies eine Spielzeugreduktion ist, und ich mag es nicht, wenn Leute das tun, aber es veranschaulicht das syntaktische Problem perfekt, also lasst uns damit anfangen.
Okay, wir haben also eine Erweiterung für UIControl, eine Methode, die ein Wrapper-Objekt zurückgibt. Das Problem ist nun folgendes:
let holder = UISwitch().makeHolder()
let output = holder.retrieve()
Das Ergebnis output
wird offensichtlich als UIControl eingegeben. Aber das will ich nicht. Ich möchte, dass es als UISwitch eingegeben wird, da ich mit einem UISwitch begonnen habe. OK, das klingt nach einem Generikum. Das Problem ist, ich kann nicht herausfinden, wie man das generisch macht.
Ich denke, es ist einfach, ControlHolder zu einem Generikum zu machen:
struct ControlHolder<T:UIControl> {
let control : T
init(control: T) {
self.control = control
}
func retrieve() -> T {
return self.control
}
}
Ich bin mir ziemlich sicher, dass ich diesen Teil richtig verstanden habe. Aber wie schreibe ich dann die Erweiterungsdeklaration so, dass diese generische in den tatsächlichen Typ des self
UIControl aufgelöst wird, auf dem makeHolder
aufgerufen wird?
Ich habe versucht, ein Generikum in die Erweiterung einzuführen, wobei ich dem Compiler gehorchte, bis ich es zum Kompilieren bekam:
extension UIControl {
func makeHolder<T>() -> ControlHolder<T> {
ControlHolder<T>(control: self as! T)
}
}
Aber das ist ziemlich albern und output
wird immer noch als UIControl eingegeben.
Natürlich kann ich einen weiteren Parameter hinzufügen, der den Typ explizit übergibt makeHolder
und ihn somit auflöst:
extension UIControl {
func makeHolder<T>(ofType: T.Type) -> ControlHolder<T> {
ControlHolder<T>(control: self as! T)
}
}
Wenn ich jetzt anrufe, gebe makeHolder
ich den Typ ein:
let holder = UISwitch().makeHolder(ofType: UISwitch.self)
let output = holder.retrieve()
Und jetzt wird natürlich output
als UISwitch getippt. Aber das ist idiotisch! Ich möchte, dass die Erweiterung nur weiß, dass der Typ UISwitch ist, da ich makeHolder
einen UISwitch aufrufe.
Ich habe das Gefühl, dass ich das alles falsch verstehe. Vielleicht kann mich jemand begradigen? Oder strebe ich etwas an, das einfach unmöglich ist?
Der Trick dabei besteht darin, ein Protokoll, eine Erweiterung dieses Protokolls, zu definieren und die makeHolder
Methode in diese Erweiterung einzufügen. Auf diese Weise können Sie Self
als generischen Typ für die Rückgabe verwenden ControlHolder
.
Definieren Sie zuerst ein neues Protokoll (nennen wir es " HoldableControl
") und fordern Sie, dass Konformere UIControl
s sein müssen. Es sind keine weiteren Anforderungen erforderlich, da wir nur die makeHolder
Funktion einer Erweiterung hinzufügen möchten.
protocol HoldableControl: UIControl {}
Fügen Sie dann eine Erweiterung von hinzu HoldableControl
und definieren Sie makeHolder
sie, wobei Sie zurückkehren ControlHolder<Self>
. Wir dürfen Self
hier verwenden, da dies in Protokollerweiterungen im Gegensatz zu einer Erweiterung auf zulässig ist UIControl
.
extension HoldableControl {
func makeHolder() -> ControlHolder<Self> {
ControlHolder(control: self)
}
}
Dann müssen wir uns nur noch UIControl
an dieses Protokoll halten:
extension UIControl: HoldableControl {}
Und machen Sie Ihr ControlHolder
Generikum, wie Sie es bereits getan haben:
struct ControlHolder<T: UIControl> {
let control: T
init(control: T) {
self.control = control
}
func retrieve() -> T {
control
}
}
Und jetzt wird es funktionieren:
let holder = UISwitch().makeHolder() // type is ControlHolder<UISwitch>
let output = holder.retrieve() // type is UISwitch
Dieser Artikel stammt aus dem Internet. Bitte geben Sie beim Nachdruck die Quelle an.
Bei Verstößen wenden Sie sich bitte [email protected] Löschen.
Lass mich ein paar Worte sagen