2016-05-13 71 views
15

採取類型名稱在C#中,有可能通過指定類型調用一個泛型方法:通用功能斯威夫特

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

var x = f<string>() 

斯威夫特不允許你調用它時專門的通用方法。編譯器要依靠類型推斷,所以這是不可能的:

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

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

我需要的是一個的方式來傳遞一個類型的函數,該函數返回該類型的對象,使用泛型

這工作,但它不是一個很適合我想做的事:

let x = f() as String? 

編輯(澄清)

我可能並不十分清楚問題實際是什麼,它只是用於調用返回給定類型(任何類型)的函數的簡單語法。

舉一個簡單的例子,假設你有任何一個數組,你創建一個返回給定類型的第一個元素的功能:

// 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 
} 

你可以調用這個函數是這樣的:

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

let x = findFirst(array) as String? 

這很簡單,但如果返回的類型是一些協議與方法,並且要調用該方法返回的對象:

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

該語法只是尷尬。在C#中(與Swift類似),你可以這樣做:

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

不幸的是,這在Swift中是不可能的。

所以問題是:有沒有一種方法可以用更簡潔(不太笨拙)的語法來實現這一點。

+0

someFunc()去尋找所提供的類型的對象,並將其返回。就這麼簡單。我想提供最少量的信息,因爲具體細節和「爲什麼」是無關緊要的。 –

+0

@PhilippeLeybaert請你澄清你的問題。你說'T'可以是任何協議,但是你不能創建一個協議的實例。協議是抽象的 - 你只能創建一個符合給定協議的* concrete *類型的實例。 「具體」與這個問題絕對相關。我不確定是否嘗試將函數輸出與給定類型進行比較,並且只有在符合該類型的情況下才會返回它,或者如果您嘗試創建傳入的給定類型的新實例。 – Hamish

+0

我不需要創建實例。該函數將查找符合該協議的對象,然後將其返回。不需要創建新的對象。 –

回答

25

不幸的是,你不能明確地定義一個通用函數的類型(通過使用它的<...>語法)。但是,可以向該函數提供一個通用元類型(T.Type)作爲參數,以便允許Swift推斷函數的通用類型,如Roman has said

爲了您的具體的例子,你會希望你的函數是這個樣子:

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

這裏,我們爲了得到已成功轉換爲T元素序列使用flatMap(_:),和然後first獲得該序列的第一個元素。我們還使用lazy,這樣我們可以在找到第一個元素後停止評估元素。

用法示例:

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() 

請注意,您必須使用.self,以指代特定類型的元類型(在這種情況下,SomeProtocol)。也許並不像你想要的語法那麼光鮮,但我認爲它跟你將要獲得的一樣好。

雖然這是值得注意的在這種情況下,該功能將更好地擺在Sequence擴展:

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

這是完全**我正在尋找的答案。 –

+0

雖然這個語法比我想出來的更加尷尬,但這正是我想知道的。謝謝! –

+0

@PhilippeLeybaert樂意幫忙:) – Hamish

3

什麼你可能需要做的就是創建一個協議看起來是這樣的:

protocol SomeProtocol { 
    init() 
    func someProtocolMethod() 
} 

,然後添加T.Type在你的方法參數:

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

然後假設你有一種符合如下SomeProtocol的類型:

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

然後,您可以調用你的函數是這樣的:

f(MyType.self).someProtocolMethod() 

像其他人指出,這似乎是一個令人費解的方式做你想做的。如果你知道的類型,例如,你可以這樣寫:

MyType().someProtocolMethod() 

沒有必要爲f

+0

當然,f()做了一些事情,所以我需要f():-) –

+0

要清楚,T可以是任何協議 –

+0

T不是協議。 T是一種類型。你不能實例化一個協議。你只能實例化類型。你寫的'f'方法只返回一個對象。我們只能脫離問題中的信息:) –