我在Swift中遇到了一些泛型问题。我有一个通用结构:
struct MyStruct<T> { ... }
我想将它存储在一个集合中(在本例中是一个字典):
var myStructDict = [MyKeyType: MyStruct]()
您会注意到我没有为T
指定MyStruct
的类型。那是因为我想存储任何MyStruct
,与T类型无关。
好吧,你说,Any
使用T
([MyKeyType: MyStruct<Any>]
)。是的,但我也喜欢在从字典中获取结构时保留每个结构的T
的原始类型信息,以便当一个函数采用{{1}时调用它时,使用正确的MyStruct
类型调用它。
以下是一个例子:
T
我保证每种类型// Setup
var myStructDict: [String: MyStruct<Any>]
func f(v: MyStruct<String>) { ... }
func f(v: MyStruct<Int>) { ... }
// Set value
let s1 = MyStruct<String>(...)
myStructDict["key"] = unsafeTypeCast(s, MyStruct<Any>.self)
// Get value
let s2 = myStructDict["key"]
// Call function
f(s2) // I want the function that takes a `MyStruct<String>` to be called
都会有一个函数。
我可以使用允许的每种类型的switch语句,如下所示:
T
但这并不是一个好主意,因为switch s1 {
case let v as MyStruct<String>: f(v)
...
}
,嗯......,是任何类型。
答案 0 :(得分:5)
通用类型在Swift中不统一。 MyStruct<Int>
和MyStruct<String>
的类型完全不同,Int
和String
也是如此,无论它们看起来多么相似。这是类型保留泛型系统的一个属性(与您在Java中找到的类型擦除泛型系统相对)。
不同种类的泛型系统可用于不同的事物。类型保留泛型是最好的,当你开始专业化并按照更通用的代码工作时 - 即编写符合协议的特定类型,然后编写使用这些协议的泛型函数或类型,或使用泛型创建可包含的包装类型其他类型的价值观,而不关心这些类型是什么。
另一方面,需要从泛型类型返回到内部专用类型或尝试创建相关泛型类型的泛化的用例可能不那么简单。
您正在寻找的内容需要两个部分:
MyStruct<String>
和MyStruct<Int>
具有共同类型祖先的方法,因此您可以将所述类型声明为字典中的元素类型MyStruct
的专用类型进行调度不要概括类型参数,概括整个类型:也就是说,不要制作[MyKeyType: MyStruct<Any>]
字典,制作[MyKeyType: MyValueType]
字典,其中MyValueType
是MyStruct<Int>
和MyStruct<String>
符合的协议。 (您可以使用Any
作为您的值类型,但是您的字典中的内容不是MyStruct
的。)例如:
protocol MyType {
func doNothing()
}
struct MyStruct<T>: MyType {
let thing: T
func doNothing() {}
}
let a = MyStruct(thing: "Hello")
let b = MyStruct(thing: 1)
let dict: [String: MyType] = ["a": a, "b": b]
doNothing
函数中有一个奇怪的地方。我们不能让成员thing
成为将MyValueType
与Any
分开的协议要求,因为该成员的类型是通用的 - 并且不能使用具有关联类型要求的协议作为具体类型(例如,作为字典元素类型的声明)。大概你的真实MyStruct
做的不仅仅是持有一个通用的thing
,所以也许你可以使用该类型的其他一些独特的特征来创建一个只有它符合的协议。
我保证每种类型
T
都会有一个函数。
说出你想要的一切,但是编译器有自己的手指并且没有听。换句话说,这是一种“保证”,你不能用语言强制执行,因此Swift不会让你做那些从这个假设出发的事情。
相反,在调用函数f
之前,您需要一个调度机制来通过强制类型从通用类型中恢复特化:
func f(v: MyStruct<String>) { print("String \(v)") }
func f(v: MyStruct<Int>) { print("Int \(v)") }
func f(v: MyType) {
switch v {
case let str as MyStruct<String>:
f(str)
case let num as MyStruct<Int>:
f(num)
default:
fatalError("unsupported type")
break
}
}
默认情况是您的“保证”发挥作用的地方。就语言而言,没有办法使这个switch
详尽无遗,因此您需要进行运行时测试以确保您没有尝试在比您声称定义的更多类型上调用f
所以,确实,您对需要switch
的猜测是正确的。但 是一个好主意,因为它(或类似的东西)是消除泛型类型歧义的唯一方法。您至少可以将其限制为需要处理MyType
协议,而不是Any
。