2016-11-03 41 views
1

我有一個協議,其功能可以返回String[String: String]。這是我的聲明:如何從協議實現中確定通用類型

protocol Test { 
    associatedtype T: Hashable 
    func returnSomething() -> T 
} 

然後我想爲returnSomething的默認實現,所以我做了一個協議擴展:

extension Test { 
    func returnSomething() -> T { 
     let valueToReturn = readValueFromPLISTthatCanReturnAStringOrDictionary() as T 
     return valueToReturn 
    } 
} 

所以最後我有2個clases,TestStringTestDictionary這兩個工具Test協議我想指出T參數,我想使用默認實現。我如何做到這一點?

class TestString: Test {} 

class TestDictionary: Test { } 

class TestString: Test where Test.T = String or similar? 
+0

你的問題是很抽象的,我不完全理解它。你能否詳細說明一下? – Alexander

+0

如果您不覆蓋它,總是使用默認實現,所以我不確定問題是什麼 – Alexander

+0

我想使用默認實現,但我無法使用它,因爲TestString和TestDictionary不會告訴測試協議,泛型類型是T. – Godfather

回答

2

我有一個協議有一個函數,可以返回一個字符串或[String:String]。這是我的聲明:

沒問題。我們寫下來。

enum StringOrDictionary { 
    case string(String) 
    case dictionary([String: String]) 
} 

protocol Test { 
    func returnSomething() -> StringOrDictionary 
} 

然後我想爲returnSomething的默認實現,所以我做了一個協議擴展:

聽起來不錯。我假設readValueFromPLISTthatCanReturnAStringOrDictionary()實際上返回Any,因爲這是propertyList(from:)返回的內容。

extension Test { 
    func returnSomething() -> StringOrDictionary { 
     let value = readValueFromPLISTthatCanReturnAStringOrDictionary() 

     switch value { 
     case let string as String: return .string(string) 
     case let dictionary as [String: String]: return .dictionary(dictionary) 
     default: fatalError() // Or perhaps you'd like to do something else 
     } 
    } 
} 

這很可能是不錯的命名類型的東西比StringOrDictionary比更有意義,但另一方面,它應該是相當簡單的。只需製作一種表達你所說的話的類型即可。你想要一個類型意味着「或」,這是一個枚舉。 (如果你想有一個類型的,意思是「AND」,這是一個結構BTW)


關於你的答案,這是不合法的:

class RandomClass: Test where Test.T == String { 
    func getValue() { 
     let bah = doSomething() // I don't need here to specify bah's type. 
    } 
} 

的方式來定義你的T是落實所需的方法。

class RandomClass: Test { 
    func returnSomething() -> String { 
     return "" 
    } 
} 

如果您想分享一些通用代碼,那麼您可以將它附加爲擴展名而不是默認實現。你可以寫一個returnString()方法並從RandomClass.returnSomething()中調用它。這在某些情況下非常有用,但我絕對不會在這種情況下使用它。你不是指「返回任何可能的類型(T)」。你的意思是「返回兩種可能類型之一」,這是一個枚舉,而不是泛型。

更新:顯然他們已經添加了一個他們已經提到的新功能,但我認爲還沒有。現在,您可以實現RandomClass這樣:

class RandomClass: Test { 
    typealias T = String 
} 

(這是一個非常不錯的新功能,即使它不是對於這個問題一個很好的答案。)

+0

,看起來不錯!似乎對我很清楚。無論如何,你能看看我的答案嗎? – Godfather

+0

美麗:)有投票權。 – Alexander

+1

我一直忘記枚舉如何像工會一樣行動 – Alexander

0

你需要,當你擴展類,像這樣指定typealias:

protocol Test { 
    associatedtype T: Hashable 
    func returnSomething() -> T 
} 

extension String: Test { 
    typealias T = Int 
} 

func def() -> Int { 
    return 6 
} 

extension Test { 
    func returnSomething() -> T { 
     return def() as! Self.T 
    } 
} 

"".returnSomething() 

但是,我無法找到一個方法來做到這一點沒有強制鑄造。

+0

這不是我所要求的。基本上我想要一個類來實現該協議,並有能力使用returnSomethingFunction。但要做到這一點,我需要告訴班級將使用哪種類型。如果我將泛型參數移動到函數中,所有工作都會問到預期,所以func testSomething () - > T然後函數知道當我讓aDict返回什麼時:[String:String] = ATestDictionary.returnSomething() – Godfather

1

這裏是你的眼前問題的解決方案:

創建2個亞型的協議,各自有不同的定義相關的類型,不同的默認實現。您可以通過在兩個子類型之間進行選擇來選擇要使用哪個默認實現。

這裏的下一個問題是[String: String]不是Hashable。這是由於缺乏對條件一致性的支持(例如,如果密鑰和值均爲Hashable,表示DictionaryHashable)是Swift最大的缺陷之一IMO的能力。你可能會想使用類型擦除包裝AnyHashable

protocol ResultProvider { 
    associatedtype Result: Hashable 
    func getResult() -> Result 
} 

protocol StringResultProvider: ResultProvider { 
    typealias Result = String 
} 

extension StringResultProvider { 
    func getResult() -> String { 
     return "A string result" 
    } 
} 

protocol IntResultProvider: ResultProvider { 
    typealias Result = Int 
} 

extension IntResultProvider { 
    func getResult() -> Int { 
     return 123 
    } 
} 

class TestIntResult: IntResultProvider {} 
class TestString: StringResultProvider {} 

print(TestString().getResult()) 
print(TestIntResult().getResult()) 


// protocol DictionaryResultProvider: ResultProvider { 
//  typealias Result = [String: String] 
// } 

// extension DictionaryResultProvider { 
//  func getResult() -> [String: String] { 
//   return ["A dictionary": "result"] 
//  } 
// } 

// class TestDictionaryProvider: DictionaryResultProvider {} 
+0

thx您!看和我的答案替代解決方案。 – Godfather

+0

使用'Any'和強制轉換來解決問題是否算作解決方案? – Alexander

+0

這是一個不好的例子返回任何,我更新了我的答案澄清。 – Godfather

0

唯一的工作解決方案是在函數中做泛型並在調用函數時指定變量類型。我在想,如果當我實現類的協議,類似於這樣我可以指定T類:

class RandomClass: Test where Test.T == String { 
    func getValue() { 
     let bah = doSomething() // I don't need here to specify bah's type. 
    } 
} 

但前面的例子就是不工作,所以另一種可能是這樣的:

protocol Test { 
    func doSomething<T>() -> T 
} 

extension Test { 
    func doSomething<T>(key: String) -> T { 
     return returnDictOrStringFromPLIST(key: key) as! T 
    } 
} 

class TestString: Test { 
    func getValue() { 
     let bah: String = doSomething() 

    } 
} 

class TestDict: Test { 
    func getValue() { 
     let bah: [String: String] = doSomething() 

    } 
}