&#39; U&#39; (通用类型)不能转换为&#39;词典<key,value =“”>&#39; </key,>

时间:2015-01-16 01:33:58

标签: generics swift dictionary

我正在尝试编写一个无论对象类型如何都可以转换输入的函数。

示例用例可能类似于:

// Example 1, Strings
In: ("foo", "bar")
Out: "foobar"

// Example 2, Integers
In: (5, 10)
Out: 15

// Example 3, Mixed (String, Array)
In: ("baz", ["foo", "bar"])
Out: ["foo", "bar", "baz"]

我解决这个问题的方法是创建一个接受泛型类型的函数,然后通过一个开关运行它们来检测它们是哪种类型,然后相应地处理它们。

该功能看起来像这样

func process<T:Hashable,U> (lhs: T, rhs: U) -> U {
    switch _stdlib_getTypeName(right) {
    case "_TtSS": // string
        // process
    case "_TtSa": // array
        // process
    case "_TtVSs10Dictionary": // dictionary
        // process
    default:
        println(_stdlib_getTypeName(rhs))
    return rhs
}

现在,上面的代码都没有涉及我的问题,但我决定将其包含在分析中,因为我可能没有以正确的方式解决我的问题。我在某处读到“如果你正在检查泛型类型的类型,你就不应该使用泛型类型。”这听起来很准确。

我一直想知道是否可能更好地使用一个字典,其中键设置为一个类型(_TtSS for String)和一个函数作为它的值。然后使用functions[_stdlib_getTypeName(right)]()运行通用变量的相应函数。但是,这仍然是检查泛型类型的类型。

所以现在我已经到了原来的问题,在我的case s中查找字符串和数组,我可以使用{{1}来访问特定于类的函数}或(right as String)。但是,当我尝试运行(right as Array)时,我遇到了错误,(right as Dictionary)

产生错误时的输入是error: 'U' is not convertible to 'Dictionary<Key, Value>'。无论密钥是["foo": "bar", "baz": "qux"]还是Int

,错误都会持续存在

知道会导致什么原因吗?

2 个答案:

答案 0 :(得分:0)

您可以为每个具有真实实现的类型构建一个专门的实例,而不是构建一个switch语句。

如果你需要一个基础(默认情况),它应该做什么,这很重要。您的选择是:

  • 让基本案例返回nil! - 然后其他所有内容都必须明确解包。如果程序失败,这将导致程序在运行时断言。虽然这是我在下面选择的,但它不一定是最佳途径。
  • 让基本案例返回nil? - 然后其他所有内容都必须手动解包。这将导致编译器坚持程序员处理nil case。
  • 除了基本情况 - 编译器将在构建时捕获缺失的案例,但您无法在其他泛型中使用process函数。
  • 在运行时具有基本案例fatalError
  • 让基本案例只返回lhs而不附加
  • 为每个lhs设置一个特殊的基本案例,其中rhs未明确支持。
  • 让基本案例只返回lhs

如果你有一组实现基本相同的类,我们可以使用协议来快捷。 DoubleStringArray的示例使用此策略。

// default case.  Can be elided, return nil, or fatalError.  I'm returning nil.
func process<T,U>(var lhs:T,rhs:U) -> T! {
    return nil // nil! will cause consuming code to assert unless they explicitly check
}

// explicit case for array + member
func process<T>(var lhs:[T],rhs:T) -> [T]! {
    lhs.append(rhs)
    return lhs
}

// explicit case for dictionary + tuple
func process<K:Hashable,V>(var lhs:[K:V],rhs:(K,V)) -> [K:V] {
    lhs[rhs.0] = rhs.1
    return lhs
}

protocol AAA {
    func +(lhs: Self, rhs: Self) -> Self
}

// case to handle the + protocol variant
func process<T:AAA>(lhs:T,rhs:T) -> T! {
    return lhs + rhs
}

// indicate already conforming classes
extension Double:AAA {}
extension Int:AAA {}
extension Array:AAA {}
extension String:AAA {}    

process("a", "b")     // -> "ab"
process(["a"], "b")   // -> ["a","b"]
process(["a"], ["b"]) // -> ["a","b"]
process(1,3)         // -> 4
process(1.0,3.0)     // nil - no implementation
process(NSDate(),"grr") //  nil - no implementation
process(["a":1],("b",2))  // ["a":1,"b":2]

如果你想要一个中缀操作符而不是一个函数(如你的注释中所建议的那样,只需添加操作符声明:

infix operator +++ {}

然后用运算符替换process的所有实例,例如+++,然后按如下方式调用它:

   [1,2,3] +++ 1
   [1,2,3] +++ [4]
   1 +++ 9

答案 1 :(得分:0)

切换方法

在我的另一个答案中,我列出了泛型的案例,但还有另一种方法,使用switch downcasting

这里我们感兴趣的是表达两种类型组合的输出。

switch语句可以为我们处理类型匹配:

func process2<T:Any,U:Any>(var _lhs: T, _rhs: U) -> T {

    switch (_lhs, _rhs) {
    case (var lhs as String, let rhs as String): // string
        return lhs + rhs as T
    case (var lhs as Array<U>, let rhs): // array
        lhs.append(rhs)
        return lhs as T
    case let (lhs as Double, rhs as Double):
        return lhs + rhs as T
    case let (lhs as Int, rhs as Int):
        return lhs + rhs as T
    default:
        println("default case")
        return _lhs
    }
}

所以让我们使用它:

process2(["foo"],"bar") // ["foo","bar"]
process2(1.0,2.0)       // 3.0
process2("a","b")       // "ab"
process2(1,2)           // 3

我还没有找到如何做数组+数组或字典+任何东西