2016-05-13 5 views
15

In C# nehmen, dann ist es möglich, eine generische Methode aufzurufen durch Angabe des Typs:generische Funktion einen Typnamen in Swift

public T f<T>() 
{ 
    return something as T 
} 

var x = f<string>() 

Swift erlaubt Ihnen nicht, eine generische Methode zu spezialisieren, wenn es aufgerufen wird. Der Compiler will auf Typinferenz verlassen, so ist dies nicht möglich:

func f<T>() -> T? { 
    return something as T? 
} 

let x = f<String>() // not allowed in Swift 

Was ich brauche, ist eine Möglichkeit, eine Art zu einer Funktion und diese Funktion zu übergeben ein Objekt dieser Art Rückkehr, unter Verwendung von Generika

Dies funktioniert, aber es ist nicht eine gute Passform für das, was ich tun möchte:

let x = f() as String? 

EDIT (KLARSTELLUNG)

Ich war mir wahrscheinlich nicht ganz im Klaren darüber, was die Frage eigentlich ist, es geht um eine einfachere Syntax zum Aufruf einer Funktion, die einen gegebenen Typ (jeden Typ) zurückgibt.

Als einfaches Beispiel, sagen wir mal Sie eine Reihe von irgendwelchen haben und Sie erstellen eine Funktion, die das erste Element eines bestimmten Typs zurückgibt:

// returns the first element in the array of that type 
func findFirst<T>(array: [Any]) -> T? { 
    return array.filter() { $0 is T }.first as? T 
} 

Sie diese Funktion wie folgt aufrufen können:

let array = [something,something,something,...] 

let x = findFirst(array) as String? 

das ist ziemlich einfach ist, aber was ist, wenn der Typ einige Protokoll mit einer Methode zurückgegeben wird und Sie die Methode auf das zurückgegebene Objekt aufzurufen:

(findFirst(array) as MyProtocol?)?.SomeMethodInMyProtocol() 
(findFirst(array) as OtherProtocol?)?.SomeMethodInOtherProtocol() 

Diese Syntax ist nur peinlich. In C# (die als Swift ebenso stark typisiert ist), können Sie dies tun:

findFirst<MyProtocol>(array).SomeMethodInMyProtocol(); 

Leider, das ist nicht möglich, in Swift.

Die Frage ist also: gibt es eine Möglichkeit, dies mit einer saubereren (weniger umständlich) Syntax zu erreichen.

+0

someFunc() geht für ein Objekt vom Typ der Suche zur Verfügung gestellt und gibt es zurück. So einfach ist das. Ich wollte die erforderliche Mindestanzahl an Informationen bereitstellen, da die Besonderheiten und das "Warum" irrelevant sind. –

+0

@PhilippeLeybaert Bitte können Sie Ihre Frage klären. Sie sagen, dass 'T' ein beliebiges Protokoll sein kann, aber Sie können keine Instanz eines Protokolls erstellen. Protokolle sind abstrakt - Sie können immer nur eine Instanz eines konkreten Typs erstellen, der einem bestimmten Protokoll entspricht. Die "Besonderheiten" sind definitiv relevant für diese Frage. Ich bin mir nicht sicher, ob Sie eine Funktionsausgabe mit einem bestimmten Typ vergleichen und nur zurückgeben wollen, wenn sie diesem Typ entspricht - oder wenn Sie versuchen, eine neue Instanz eines bestimmten Typs zu erstellen, den Sie übergeben. – Hamish

+0

Ich brauche keine Instanz zu erstellen. Die Funktion sucht nach Objekten, die diesem Protokoll entsprechen, und gibt sie dann zurück. Es müssen keine neuen Objekte erstellt werden. –

Antwort

25

Leider können Sie den Typ einer generischen Funktion nicht explizit definieren (indem Sie die Syntax 10 verwenden). Sie können jedoch einen generischen Metatyp (T.Type) als ein Argument an die Funktion bereitstellen, um Swift zu ermöglichen, den generischen Typ der Funktion abzuleiten, wie Roman has said.

Für Ihr spezielles Beispiel, werden Sie Ihre Funktion in etwa so aussehen wollen:

func findFirst<T>(in array: [Any], ofType _: T.Type) -> T? { 
    return array.lazy.flatMap{ $0 as? T }.first 
} 

Hier sind wir flatMap(_:), um eine Folge von Elementen zu erhalten, die erfolgreich zu T gegossen wurden verwendet und dann first, um das erste Element dieser Sequenz zu erhalten. Wir verwenden auch lazy, so dass wir die Auswertung von Elementen stoppen können, nachdem wir die erste gefunden haben.

Beispiel Nutzung:

protocol SomeProtocol { 
    func doSomething() 
} 

protocol AnotherProtocol { 
    func somethingElse() 
} 

extension String : SomeProtocol { 
    func doSomething() { 
     print("success:", self) 
    } 
} 

let a: [Any] = [5, "str", 6.7] 

// outputs "success: str", as the second element is castable to SomeProtocol. 
findFirst(in: a, ofType: SomeProtocol.self)?.doSomething() 

// doesn't output anything, as none of the elements conform to AnotherProtocol. 
findFirst(in: a, ofType: AnotherProtocol.self)?.somethingElse() 

Beachten Sie, dass Sie .self verwenden, um auf die Metatyp eines bestimmten Typs (in diesem Fall SomeProtocol) zu verweisen. Vielleicht nicht so glatt wie die Syntax, die Sie anstrebten, aber ich denke, es ist ungefähr so ​​gut, wie Sie bekommen werden.

Obwohl es sich lohnt, in diesem Fall der Feststellung, dass die Funktion besser in einer Verlängerung Sequence gelegt würde:

extension Sequence { 
    func first<T>(ofType _: T.Type) -> T? { 
    // Unfortunately we can't easily use lazy.flatMap { $0 as? T }.first 
    // here, as LazyMapSequence doesn't have a 'first' property (we'd have to 
    // get the iterator and call next(), but at that point we might as well 
    // do a for loop) 
    for element in self { 
     if let element = element as? T { 
     return element 
     } 
    } 
    return nil 
    } 
} 

let a: [Any] = [5, "str", 6.7] 
print(a.first(ofType: String.self) as Any) // Optional("str") 
+0

Das ist ** genau ** die Antwort, die ich suchte. –

+0

Obwohl diese Syntax noch unbeholfener ist als die, die ich gefunden habe, ist es genau das, was ich wissen wollte. Vielen Dank! –

+0

@PhilippeLeybaert Glücklich zu helfen :) – Hamish

3

Was Sie wahrscheinlich tun müssen, ist ein Protokoll zu erstellen, die etwa wie folgt aussieht:

protocol SomeProtocol { 
    init() 
    func someProtocolMethod() 
} 

und fügen Sie dann T.Type als Parameter in Ihrer Methode:

func f<T: SomeProtocol>(t: T.Type) -> T { 
    return T() 
} 

Dann vorausgesetzt, Sie haben ein Typ, der SomeProtocol wie folgt entspricht:

struct MyType: SomeProtocol { 
    init() { } 
    func someProtocolMethod() { } 
} 

Sie können Ihre Funktion wie folgt dann rufen:

f(MyType.self).someProtocolMethod() 

Wie andere bemerkt haben, dies scheint wie eine gewundene Weg zu tun, was Sie wollen.Wenn Sie die Art, zum Beispiel wissen, könnten Sie einfach schreiben:

MyType().someProtocolMethod() 

Es besteht keine Notwendigkeit für f ist.

+0

Natürlich tut f() etwas, also würde ich f() benötigen :-) –

+0

Um klar zu sein, kann T jedes Protokoll sein –

+0

T ist kein Protokoll. T ist ein Typ. Sie können ein Protokoll nicht instanziieren. Sie können nur Typen instanziieren. Die 'f'-Methode, die Sie geschrieben haben, gibt nur ein Objekt zurück. Wir können nur aus den Informationen in der Frage gehen :) –

Verwandte Themen