采用数组和字典的协议

Adopt a protocol for Array and Dictionary

我有以下协议:

protocol MyProtocol {

    var stringValue: String { get }
}

我还为某些 类 和扩展中的结构实现了它的方法:

extension Int: MyProtocol {

    var stringValue: String {

        return "IntValue"
    }
}

extension String: MyProtocol {

    var stringValue: String {

        return "StringValue"
    }
}

extension Array: MyProtocol where Element == Dictionary<String, Any> {

    var stringValue: String {

        return "ArrayValue"
    }
}

extension Dictionary: MyProtocol where Key == String, Value == Any {

    var stringValue: String {

        return "DictionaryValue"
    }
}

当我尝试使用以下代码对其进行测试时:

let dict = [["key":"value"]]
let stringValueResult = dict.stringValue
print(stringValueResult)

我收到一条错误消息“[[String : String]] 无法转换为 Array<Dictionary<String, Any>>”。但是,当我像这样设置变量 dict 的类型时,我的代码工作正常:

let dict: Array<Dictionary<String, Any>> = [["key":"value"]]

谁能解释一下为什么我的第一个版本的代码无法编译?

您应该像这样更改 ArrayDictionary 实施约束:

extension Array: MyProtocol where Element: MyProtocol {

    var stringValue: String {

        return "ArrayValue"
    }
}

extension Dictionary: MyProtocol where Key == String {

    var stringValue: String {

        return "DictionaryValue"
    }
}

现在,如果您 运行 以下代码,它将打印:ArrayValue

let dict = [["key":"value"]]
let stringValueResult = dict.stringValue
print(stringValueResult)

此错误的原因是编译器推断的 dict 类型与您在 Array 符合 protocol 时提到的类型之间存在冲突。

写这行的时候

let dict = [["key":"value"]]  

对于编译器,它将变成如下类型为 Array<Dictionary<String, String>>

let dict: Array<Dictionary<String, String>> = [["key":"value"]]

现在,当您执行 dict.stringValue 时,编译器将首先匹配调用对象的类型,即 dict 与您在 Array 与 [=20= 的一致性期间定义的类型]

由于编译器推断类型,即 Array<Dictionary<String, String>> 不同于您在一致性中提到的类型,即 Array<Dictionary<String, Any>>,因此编译器会抛出错误。

但是当您声明 dict 具有显式类型的变量时,即 Array<Dictionary<String, Any>> 与您根据 MyProtocol 定义的相同,那么编译器看不到任何问题并且您的代码工作正常.

虽然 Array/Dictionary 符合 MyProtocol,但您可以忽略设置 Element 类型,如下所示,

extension Array: MyProtocol {

    var stringValue: String {

        return "ArrayValue"
    }
}

extension Dictionary: MyProtocol {

    var stringValue: String {

        return "DictionaryValue"
    }
}