2014-07-02 11 views
16

In meiner Anwendung Ich möchte eine generische Methode schaffen, die ein Array von Objekt erstellt depening auf dem angegebenen Typ T.Generics rufen mit Typ T in Swift

ich die folgende Funktion erstellt:

func getArray<T : ROJSONObject>(key:String) -> T[] { 
    var elements = T[]() 

    for jsonValue in getValue(key).array! { 
     var element = T() 

     element.jsonData = jsonValue 
     elements.append(element) 
    } 

    return elements 
} 

Jetzt möchte ich den Typ übergeben, wenn ich die Methode aufruft, also weiß sie, welchen Typ sie intern erzeugen soll. Ich denke, in Java und C# Sie eine Methode wie das verwenden können:

object.getArray<Document>("key") 

Wenn ich es so nennen, habe ich immer die Fehlermeldung:

Cannot explicitly specialize a generic function 

Also mein fix war ein zusätzlicher Parameter zu definieren, eine Instanz des Typs T enthält, so dass es automatisch die Art erkennt:

func getArray<T : ROJSONObject>(key:String, type:T) -> T[] { 
    var elements = T[]() 

    for jsonValue in getValue(key).array! { 
     var element = T() 

     element.jsonData = jsonValue 
     elements.append(element) 
    } 

    return elements 
} 

gibt es wirklich keine andere Möglichkeit, dieses Verhalten zu erhalten, ohne eine nicht verwendete Instanz zu übergeben? Oder unterscheide ich etwas falsch?

Weitere Tests

Nach der Antwort von jtbandes ich einige Tests gemacht haben. Ich habe versucht, den Typ zu erzwingen, indem ich die as in dem Anruf hinzufüge.

class Person { 

    init() { } 

    func getWorkingHours() -> Float { 
     return 40.0 
    } 
} 

class Boss : Person { 
    override func getWorkingHours() -> Float { 
     println(100.0) 
     return 100.0 
    } 
} 

class Worker : Person { 
    override func getWorkingHours() -> Float { 
     println(42.0) 
     return 42.0 
    } 
} 

func getWorkingHours<T : Person>() -> T { 
    var person = T() 
    person.getWorkingHours() 

    return person 
} 

var worker:Worker = getWorkingHours() as Worker 
var boss:Boss = getWorkingHours() as Boss 
worker.getWorkingHours() // prints out 40.0 instead of 42.0 
boss.getWorkingHours() // prints out 40.0 instead of 100.0 

Also irgendwie ist der Typ immer der Basistyp selbst habe ich den Typen mit dem as Schlüsselwort angegeben. Ich weiß, dass das Beispiel nicht viel Sinn macht, aber es war nur für Testzwecke.

+1

Oh nein Swift Typ System ist komplett kaputt (noch einmal). Außerdem "funktioniert" es, wenn die Basisklasse von NSObject geerbt wird. [Code hier] (https://gist.github.com/xlc/ba7f213e1f94cc1cada5) –

+0

Die Antwort ist sehr einfach. Sie müssen den Typ irgendwie angeben, also tun Sie es als 'object.getArray (" Schlüssel ") als [Dokument]' – thesummersign

Antwort

4

Ich denke, es ist ein Fehler.

Sie können, indem die Klasse eine Unterklasse von NSObject oder markieren Konstruktor Basisklasse um es arbeiten mit @required

import Cocoa 

class A : NSObject { 
    init() { } 
} 
class B : A {} 
class C : A {} 

func Create<T:NSObject>() -> T { 
    return T() 
} 

println(Create() as A) 
println(Create() as B) 
println(Create() as C) 

//<_TtC11lldb_expr_01A: 0x7f85ab717bc0> 
//<_TtC11lldb_expr_01B: 0x7f85ab451e00> 
//<_TtC11lldb_expr_01C: 0x7f85ab509160> 

class D { 
    @required init() { } 
} 

class E : D { 
    init() { } 
} 

class F : D { 
    init() { } 
} 

func Create2<T:D>() -> T { 
    return T() 
} 

println(Create2() as D) 
println(Create2() as E) 
println(Create2() as F) 

//C11lldb_expr_01D (has 0 children) 
//C11lldb_expr_01E (has 1 child) 
//C11lldb_expr_01F (has 1 child) 

nicht sicher, warum @required das Problem lösen. Aber this is the reference

required

Apply this attribute to a designated or convenience initializer of a class to indicate that every subclass must implement that initializer.

Required designated initializers must be implemented explicitly. Required convenience initializers can be either implemented explicitly or inherited when the subclass directly implements all of the superclass’s designated initializers (or when the subclass overrides the designated initializers with convenience initializers).

+1

Danke, mit dem @ required konnte ich mein Problem lösen! – Prine

6

Sie sollten in der Lage sein, den Typ mit dem as Schlüsselwort anzugeben: object.getArray("key") as Document[].

+0

Danke für Ihre Antwort. Ich habe meine Frage bearbeitet und Ihre Lösung ausprobiert. Aber irgendwie bleibt es immer bei der Basisklasse und erzeugt intern kein Objekt der angegebenen Klasse mit dem Schlüsselwort "as". – Prine

+0

Ich kann jetzt nicht mehr testen, aber Sie könnten [einen Fehler einreichen] (http://bugreport.apple.com). – jtbandes

+0

Ich habe einen Fehlerbericht eingereicht. Mal sehen, was Apple antwortet. Werde es hier posten, wenn ich eine Antwort bekomme. – Prine

14

arbeitete ich um diesen von unsafeBitCast aus der schnellen Laufzeitfunktion zu leihen.

Es ist als func unsafeBitCast<T, U>(x: T, _: U.Type) -> U deklariert und Sie können es als unsafeBitCast(value, MyType) nennen.

auf Ihre Funktion Angewandte diese

func getArray<T : ROJSONObject>(key:String, _: T.Type) -> T[] { 
    // function stays the same 
} 

wäre Und Sie können es wie dieses

getArray("key", Document) 
+2

Ich denke, der richtige Aufruf wäre getArray ("Schlüssel", Document.self) – Ciprian

+0

Nur wenn die generische Funktion mehr als einen Parameter erhalten. Wenn es nur einen Parameter erwartet, erlaubt es, Dokument – shinji14

+1

zu übergeben Es ist so komisch, dass Sie es so als ein Parameter übergeben müssten. Das getArray macht aus Lesbarkeitsgründen mehr Sinn. Das hat aber funktioniert, danke! – JamWils

2

Sie müssen required initializer machen rufen, dann let realType = T.self Zeile hinzufügen und T() mit realType() ersetzen.

class Person { 
    required init() { } 

    func getWorkingHours() -> Float { 
     return 40.0 
    } 
} 

class Boss : Person { 
    override func getWorkingHours() -> Float { 
     println(100.0) 
     return 100.0 
    } 
} 

class Worker : Person { 
    override func getWorkingHours() -> Float { 
     println(42.0) 
     return 42.0 
    } 
} 

func getWorkingHours<T : Person>() -> T { 
    let realType = T.self 
    var person = realType() 
    person.getWorkingHours() 

    return person 
} 
0

Um den ersten Teil Ihrer Frage zu beantworten

Sie benötigen keine zusätzliche Parameter an die Methode fügen Sie die Art von T wie Sie bereits Fledermaus [T] als Rückgabetyp angeben

so ersetzen Sie diese Linie

object.getArray<Document>("key") 

mit diesem

object.getArray("key") as [Document] 

Auf diese Weise erhalten Sie die Möglichkeit, den Typ T anzugeben.

0

Sie es nicht wie

nennen
object.getArray<Document>("key") 

Stattdessen lassen Sie den "<Dokument>" Teil, wie folgt aus:

object.getArray("key") 

für mich das Problem behoben.

0

Arbeits Version des Codes in der Frage (zweiter Teil)

• Init ist erforderlich, da eine Funktion, um eine generische Person Art ( newPerson(as person:T) -> T) liefert

• ich die Klassenmethoden ersetzt haben func getWorkingHours() von var workingHours { get } .. Es funktioniert gleichermaßen, in einer 'swifty' Art und Weise.

getWorkingHours() kehrt Stunden, wie der Name ..

class Person { 
    required init() {} 
    var workingHours: Float { get { return 40 } } 
} 

class Boss : Person { 
    override var workingHours: Float { get { return 100 } } 
} 

class Worker : Person { 
    override var workingHours: Float { get { return 42 } } 
} 

func getWorkingHours<T : Person>(_ person: T) -> Float { 
    return person.workingHours 
} 

func newPerson<T : Person>(as person: T) -> T { 
    return T() 
} 

// ----------------------------------------------- 

let worker = Worker() 
let boss = Boss() 

print(getWorkingHours(worker)) // prints 42.0 
print(getWorkingHours(boss)) // prints 100.0 

let person = newPersonOfSameClass(as: worker) 

print(getWorkingHours(person)) // prints 42.0 

Hoffnung, keine Person gibt es hilft .. Dieser Code für Kopieren/Einfügen in Spielplatz bereit ist.

Verwandte Themen