我正在为具有func
结构的结构编写测试,该结构的子参数为Result<ModelProtocol>
。
但是,当我为我的struct编写模拟时,它拒绝存储关于<T> != <ModelProtocol>
的闭包。反过来说这是正确的,因为这是一种通用类型。
我现在得到的错误是:
Playground execution failed:
error: Test.playground:51:49: error: cannot assign value of type '(Result<T>) -> Void' to type '((Result<ModelProtocol>) -> Void)?'
self.doSomethingCompletionHandler = completionHandler
^~~~~~~~~~~~~~~~~
问题是什么,因为<T>
实际上属于T: ModelProtocol
类型。
我如何存储闭包(completionHandler),以便稍后调用它来手动运行闭包(通过测试)。
这是我在操场上遇到的问题的一个例子:
public enum Result<Value> {
case success(Value)
case failure(Error)
public var value: Value? {
switch self {
case .success(let value):
return value
case .failure:
return nil
}
}
public var error: Error? {
switch self {
case .success:
return nil
case .failure(let error):
return error
}
}
}
protocol ModelProtocol {
init(aString: String)
}
protocol AnImportantProtocol {
func doSomething<T: ModelProtocol>(firstParameter: String, completionHandler: @escaping((Result<T>)->Void))
}
enum StructureError : Error {
case defaultError
}
struct StructureOne : AnImportantProtocol {
func doSomething<T: ModelProtocol>(firstParameter: String, completionHandler: @escaping ((Result<T>) -> Void)) {
debugPrint("Doing something")
if let model = ExampleModel(aString: "Test") as? T {
completionHandler(.success(model))
} else {
completionHandler(.failure(StructureError.defaultError))
}
}
}
class StructureOneMock : AnImportantProtocol {
var doSomethingInvokeCount: Int = 0
var doSomethingCompletionHandler: ((Result<ModelProtocol>)->Void)? = nil
func doSomething<T: ModelProtocol>(firstParameter: String, completionHandler: @escaping ((Result<T>) -> Void)) {
self.doSomethingInvokeCount += 1
self.doSomethingCompletionHandler = completionHandler
}
func callCompletionHandler(result: Result<ModelProtocol>) {
if let doSomethingCompletionHandler = self.doSomethingCompletionHandler {
doSomethingCompletionHandler(result)
}
}
}
struct ExampleModel {
let someString: String
}
extension ExampleModel : ModelProtocol {
init(aString: String) {
self.someString = aString
}
}
答案 0 :(得分:1)
在这种情况下,最好使用associatedType约束,例如:
protocol AnImportantProtocol {
associatedtype MyType: ModelProtocol
func doSomething(firstParameter: String, completionHandler ((Result<MyType>)->Void)?)
}
然后在实现中输入typealias:
class StructureOneMock<T: ModelProtocol> : AnImportantProtocol {
typealias MyType = T
var doSomethingInvokeCount: Int = 0
var doSomethingCompletionHandler: ((Result<MyType>)->Void)? = nil
func doSomething(firstParameter: String, completionHandler: ((Result<MyType>) -> Void)?) {
self.doSomethingInvokeCount += 1
self.doSomethingCompletionHandler = completionHandler
}
func callCompletionHandler(result: Result<MyType>) {
if let doSomethingCompletionHandler = self.doSomethingCompletionHandler {
doSomethingCompletionHandler(result)
}
}
}
您可以跳过实现中的泛型T并在那里指定具体类型。
答案 1 :(得分:1)
我相信你有一个不明显的逆变误差,因为方差发生在泛型参数中。
请考虑以下代码:
class Cat {}
class Kitten: Cat {}
class Cougar: Cat {}
protocol CatDayCareProtocol {
func setRaiseFunction(raiseFunction: @escaping (Cat) -> Cougar)
}
class CatDayCare: CatDayCareProtocol {
func setRaiseFunction(raiseFunction: @escaping (Cat) -> Cougar) {
self.raiseFunction = raiseFunction
}
private var raiseFunction: ((Cat) -> Cougar)? = nil
func callCougerRaiseFunction() -> Cougar? {
let cougar = Cougar()
return raiseFunction?(cougar)
}
}
let catDayCare = CatDayCare()
catDayCare.setRaiseFunction(raiseFunction: { kitty: Kitten in
return Cougar()
})
在此示例中,swift引发以下错误:
error: Contravariance.playground:23:51: error: expected expression
catDayCare.setRaiseFunction(raiseFunction: { kitty: Kitten in
这似乎不直观,因为小猫是猫,所以这不是很好吗?让我们考虑如果您尝试执行callCougerRaiseFunction()会发生什么。它实例化一只美洲狮,它是一只猫,并调用它的加强功能,它需要一只猫,所以这是合法的。但是,如果你传递一个期望小猫作为参数的功能,突然你将美洲狮传递给想要小猫的功能,这很难过。
现在举个例子,你有
func doSomething<T: ModelProtocol>(firstParameter: String, completionHandler: @escaping ((Result<T>) -> Void)) {
self.doSomethingInvokeCount += 1
self.doSomethingCompletionHandler = completionHandler
}
在这个例子中,T严格地与ModelProtocol一样或更具体(因为它可能是从ModelProtocol继承的任何协议),我认为这使得Result<T>
作为函数参数与数据类型逆变{ {1}}。只是编译器不够聪明才能知道它是逆变,但它确实知道转换是不合法的。
至于实际解决问题,是否真的有必要使用通用?为什么你不能使用:
Result<ModelProtocol>