2016-11-03 31 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() 

    } 
}