如何从协议实现中确定泛型类型

时间:2016-11-03 17:15:13

标签: ios swift

我的协议有一个可以返回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?

4 个答案:

答案 0 :(得分:2)

  

我有一个协议,它有一个可以返回String或[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更有意义的东西可能会更好,但除此之外,它应该非常简单。只做一个意味着你说的话。你想要一个意味着“OR”的类型,这是一个枚举。 (如果你想要一个意味着“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
}

(这是一个非常好的新功能,即使它不是这个问题的好答案。)

答案 1 :(得分:1)

这是解决您眼前问题的方法:

创建协议的2个子类型,每个子类型具有不同的关联类型定义,以及不同的默认实现。您可以通过在两个子类型之间进行选择来选择您希望类使用的默认实现。

这里的下一个问题是[String: String]不是Hashable。这是由于缺乏对条件一致性的支持(例如,如果密钥和值都是Dictionary),则表示HashableHashable的能力,其中一个是Swift'最大的垮台,国际海事组织。您可能希望使用类型擦除包装器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 {}

答案 2 :(得分: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()
  

6

但是,如果没有强制投射,我无法找到办法。

答案 3 :(得分: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()

    }
}