Erhalte rawValue von einer Enumeration in einer generischen Funktion

8

Update 28.08.2015: Dies wird in Swift 2 gelöst werden

Siehe Twitter-Antwort vom Swift-Compiler-Entwickler

Update 23.10.2015: Mit Swift 2-Generika können Sie immer noch nicht den rohen Wert erhalten. Sie können den zugehörigen Wert erhalten.

Ursprüngliche Frage:

Ich habe einige allgemeine Reflexionscodes in swift geschrieben. In diesem Code habe ich Probleme, den Wert für Eigenschaften zu erhalten, die auf einer Aufzählung basieren. Das Problem ist darauf zurückzuführen, dass ich .rawValue nicht auf dem Eigenschaftswert vom Typ Any ausführen kann. Der Swift-Reflektionscode gibt den Enum-Wert als Typ Any zurück. Wie kann ich also von einem Any zu einem AnyObject gelangen, welches der rawValue der Enum ist.

Die einzige Problemumgehung, die ich bisher gefunden habe, besteht darin, alle Enums mit einem Protokoll zu erweitern. Unten sehen Sie einen Komponententest, der mit dieser Problemumgehung in Ordnung ist.

Gibt es eine Möglichkeit, dies zu lösen, ohne Code zu den ursprünglichen Enums hinzuzufügen?

für meinen Reflektionscode Ich muss die getRawValue Methodensignatur beibehalten, so wie sie ist.

%Vor%     
Edwin Vermeer 24.07.2015, 10:02
quelle

1 Antwort

10

Leider sieht das in Swift zu diesem Zeitpunkt nicht so aus, aber ich habe eine Weile über Ihr Problem nachgedacht und werde Ihnen drei Möglichkeiten vorschlagen, wie das Swift-Team Ihnen helfen könnte, dieses Problem zu lösen.

  1. Fix den Spiegel für Enums. Die einfachste Lösung ist eine, die Sie sicher schon versucht haben. Sie versuchen, eine Reflektionsbibliothek zu erstellen, und Sie möchten einen Any -Wert angeben, um zu sehen, ob es sich um eine Enumeration handelt. Wenn dies der Fall ist, möchten Sie sehen, ob sie einen Rohwert enthält. Die rawValue -Eigenschaft sollte über diesen Code zugänglich sein:

    %Vor%

Dies funktioniert jedoch nicht. Sie werden feststellen, dass der Spiegel eine count von 0 hat. Ich glaube wirklich, dass dies ein Versehen seitens des Swift-Teams bei der Umsetzung von Swift._EnumMirror ist, und ich werde ein Radar darüber einreichen. rawValue ist definitiv eine legitime Eigenschaft. Es ist ein seltsames Szenario, weil Enums andere gespeicherte Eigenschaften nicht haben dürfen. Außerdem entspricht die Deklaration Ihrer Enumeration niemals explizit RawRepresentable , noch deklariert sie die rawValue -Eigenschaft. Der Compiler sagt das nur, wenn Sie enum MyEnum: String oder : Int oder was auch immer eingeben. In meinen Tests scheint es, dass es egal ist, ob die Eigenschaft in einem Protokoll definiert ist oder eine Instanz eines zugeordneten Typs ist, es sollte immer noch eine Eigenschaft sein, die im Spiegel dargestellt wird.

  1. Protokolltypen mit definierten zugeordneten Typen zulassen. Wie bereits in meinem Kommentar erwähnt, ist es in Swift eine Einschränkung, dass Sie nicht auf einen Protokolltyp mit zugehörigen Typanforderungen umwandeln können. Sie können nicht einfach in RawRepresentable umwandeln, da Swift nicht weiß, welchen Typ die Eigenschaft rawValue zurückgibt. Syntax wie RawRepresentable<where RawValue == String> ist denkbar, oder vielleicht protocol<RawRepresentable where RawValue == String> . Wenn dies möglich wäre, könnten Sie versuchen, den Typ durch eine switch-Anweisung wie folgt zu umwandeln:

    %Vor%

Aber das ist nicht in Swift definiert. Und wenn Sie nur versuchen, in RawRepresentable zu casten, sagt Ihnen der Swift-Compiler, dass Sie das nur in einer generischen Funktion verwenden können, aber wenn ich Ihren Code betrachte, führt Sie das nur durch ein Kaninchenloch. Generische Funktionen müssen Informationen zur Kompilierzeit eingeben, um zu funktionieren, und genau das ist es, wofür Sie nicht mit Any instances arbeiten.

Das Swift-Team könnte Protokolle so ändern, dass sie mehr wie generische Klassen und Strukturen sind. Zum Beispiel sind MyGenericStruct<MyType> und MyGenericClass<MyType> rechtmäßig spezialisierte konkrete Typen, für die Sie eine Laufzeitprüfung durchführen und in die Sie casten können. Allerdings kann das Swift-Team gute Gründe dafür haben, dies nicht mit Protokollen zu tun. Spezialisierte Versionen von Protokollen (d. H. Protokollreferenzen mit bekannten zugehörigen Typen) sind immer noch keine konkreten Typen. Ich würde für diese Fähigkeit nicht den Atem anhalten. Ich halte dies für die schwächste meiner vorgeschlagenen Lösungen.

  1. Erweitern Sie Protokolle, um Protokollen zu entsprechen. Ich dachte wirklich, ich könnte diese Lösung für Sie arbeiten lassen, aber leider nein. Da Swifts eingebauter Spiegel für Enums nicht die rawValue liefert, dachte ich mir, warum ich meinen eigenen generischen Spiegel nicht implementiere:

    %Vor%

Großartig! Jetzt müssen wir nur noch RawRepresentable auf Reflectable erweitern, damit wir zuerst theEnum as Reflectable (kein zugehöriger Typ erforderlich) und dann reflect(theEnum) aufrufen können, um uns unseren fantastischen benutzerdefinierten Spiegel zu geben:

%Vor%
  

Compiler Fehler: Erweiterung des Protokolls 'RawRepresentable' kann nicht haben   eine Vererbungsklausel

Waaaaat ?! Wir können konkrete Typen erweitern, um neue Protokolle zu implementieren, zum Beispiel:

%Vor%

Ab Swift 2 können wir Protokolle erweitern, um konkrete Implementierungen von Funktionen zu geben, wie zum Beispiel:

%Vor%

Ich dachte sicher, wir könnten Protokolle erweitern, um andere Protokolle zu implementieren, aber anscheinend nicht! Ich sehe keinen Grund, warum wir Swift das nicht machen könnten. Ich denke, das würde sehr gut in das protokollorientierte Programmierparadigma passen, das die WWDC 2015 war. Konkrete Typen, die das ursprüngliche Protokoll implementieren, würden eine neue Protokollkonformität kostenlos erhalten, aber der konkrete Typ ist auch frei, seine eigenen zu definieren Versionen der Methoden des neuen Protokolls. Ich werde auf jeden Fall eine Verbesserungsanfrage einreichen, weil ich denke, dass es eine mächtige Funktion sein könnte. In der Tat, ich denke, es könnte sehr nützlich in Ihrer Reflexionsbibliothek sein.

Bearbeiten: Wenn ich an meine Unzufriedenheit mit Antwort 2 zurückdenke, denke ich, dass es eine elegantere und realistischere Möglichkeit gibt, mit diesen Protokollen im Großen und Ganzen zu arbeiten und meine Idee von Antwort 3 über das Erweitern von Protokollen kombiniert konform zu neuen Protokollen. Die Idee besteht darin, Protokolle mit zugehörigen Typen zu haben, die neuen Protokollen entsprechen, die vom Typ gelöschte Eigenschaften des Originals abrufen. Hier ist ein Beispiel:

%Vor%

Wenn Sie das Protokoll auf diese Weise erweitern, wird die Vererbung nicht per se erweitert.Eher würde es nur zum Compiler sagen: "Wo immer es einen Typ gibt, der mit RawRepresentable übereinstimmt, muss dieser Typ auch mit AnyRawRepresentable mit dieser Standardimplementierung übereinstimmen." AnyRawRepresentable hätte keine Typanforderungen, kann aber rawValue als Any abrufen. In unserem Code dann:

%Vor%

Diese Art von Lösung könnte allgemein mit jeder Art von Protokoll mit zugehörigen Typen verwendet werden. Ich werde diese Idee ebenfalls in meinen Vorschlag für das Swift-Team aufnehmen, Protokolle mit Protokollkonformität zu erweitern.

Aktualisieren : Keine der oben genannten Optionen ist ab Swift 4 verfügbar. Ich habe keine Antwort darauf erhalten, warum Mirror in RawRepresentable enum nicht rawValue enthält. . Wie bei den Optionen 2 und 3 sind sie immer noch im Bereich der Möglichkeiten für zukünftige Versionen von Swift. Sie wurden auf der Swift Mailingliste und im von Doug verfassten Generics Manifest Dokument erwähnt Gregor des Swift-Teams.

Der richtige Ausdruck für Option # 2 ("Protokolltypen mit definierten zugeordneten Typen zulassen") ist generalisierte existentielles . Dies würde es Protokollen mit zugehörigen Typen ermöglichen, möglicherweise Any , wenn ein Typ zugeordnet ist, automatisch zurückzugeben oder eine Syntax wie die folgende zuzulassen:

%Vor%

Option # 3 wurde gelegentlich auf der Mailing-Liste erwähnt, und es ist eine häufig abgelehnte angeforderte Funktion, aber das soll nicht heißen, dass sie nicht in zukünftigen Versionen von Swift enthalten sein könnte. Im Generics Manifesto heißt es "Conditional conformances via Protokollerweiterungen ". Obwohl es seine Macht als Merkmal anerkennt, stellt es leider auch fest, dass eine effiziente Implementierung "nahezu unmöglich" ist.

    
Christopher Whidden 01.08.2015, 07:14
quelle

Tags und Links