简体   繁体   English

通用结构的集合

[英]Collection of generic structs

I've run into a bit of an issue with generics in Swift. 我在Swift中遇到了泛型问题。 I have a generic struct: 我有一个通用的结构:

struct MyStruct<T> { ... }

And I want to store it on a collection (in this case a dictionary): 我想将其存储在一个集合中(在本例中为字典):

var myStructDict = [MyKeyType: MyStruct]()

You'll notice I didn't specify the type of T for MyStruct . 您会注意到我没有为MyStruct指定T的类型。 That is because I'd like to store any MyStruct , independent of the type T. 那是因为我想存储任何MyStruct ,而与T类型无关。

Fine, you say, use Any for T ( [MyKeyType: MyStruct<Any>] ). 细,你说,使用Any用于T[MyKeyType: MyStruct<Any>] Yes, but I'd also like to keep the original type information of T of each struct when I get the struct from the dictionary so that when a function that takes a MyStruct is called, it is called with the right type of T . 是的,但我想保持原来的类型信息T每个结构的,当我从字典中获得结构,这样当需要的功能MyStruct被调用时,它被称为与正确类型的T

Here's an example: 这是一个例子:

// 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

I guarantee that there'll be a function for every type T will take. 我保证每种T都会有一个函数。

I could use a switch statement with every type allowed, like so: 我可以对每个允许的类型使用switch语句,如下所示:

switch s1 {
case let v as MyStruct<String>: f(v)
...
}

But that is not at all a good idea, because Any , well..., is any type. 但这根本不是一个好主意,因为Any ,嗯,是任何类型。

Generic types don't unify in Swift. 通用类型在Swift中不统一。 MyStruct<Int> and MyStruct<String> are completely different types, as much so as Int and String are, no matter how similar they look. MyStruct<Int>MyStruct<String>是完全不同的类型,就像IntString一样,无论它们看起来多么相似。 This is one of the attributes of a type preserving generics system (as opposed to a type erasing generics system like you find in, say, Java). 这是类型保留泛型系统的属性之一(与您在Java中发现的类型擦除泛型系统相反)。

Different kinds of generics systems can be useful for different things. 不同种类的泛型系统可用于不同的事物。 Type preserving generics are best when you start out specialized and work your way to more generic code — ie write specific types that conform to protocols and then generic functions or types that make use of those protocols, or use generics to create wrapper types that can contain values of other types without caring what those types are. 保留类型的泛型是最好的,这是当您开始专门化并尝试使用更通用的代码时-即编写符合协议的特定类型,然后编写使用这些协议的泛型函数或类型,或使用泛型创建可以包含以下内容的包装类型其他类型的值,而无需关心这些类型是什么。

On the other hand, use cases where you need to work back from a generic type to the specialized thing inside, or try to create generalizations of related generic types, can be less than straightforward. 另一方面,在某些情况下,您需要从泛型类型变回内部的专用对象,或者尝试创建相关泛型类型的泛化,这种情况并不那么简单。

What you're looking for needs two parts: 您要寻找的东西需要两个部分:

  1. a way to declare that MyStruct<String> and MyStruct<Int> have a common type ancestor, so you can declare said type as the element type in a dictionary 一种声明MyStruct<String>MyStruct<Int>具有共同类型祖先的方法,因此您可以在字典中将所述类型声明为元素类型
  2. a way to dispatch based on the specialized type of a MyStruct once you pull it out of the dictionary 一旦将其从字典中拉出,一种MyStruct的特殊类型进行调度的方法

Part 1 第1部分

Don't generalize the type parameters, generalize the whole type: That is, don't make a dictionary of [MyKeyType: MyStruct<Any>] , make a dictionary of [MyKeyType: MyValueType] , where MyValueType is a protocol to which both MyStruct<Int> and MyStruct<String> conform. 不概括类型参数,概括整个类型:即,不作的字典[MyKeyType: MyStruct<Any>]使的字典[MyKeyType: MyValueType]其中MyValueType是一个协议,其既MyStruct<Int>MyStruct<String>一致。 (You could use Any for your value type, but then you'd get things in your dictionary that aren't MyStruct s.) For example: (您可以将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]

There's an oddity here in the doNothing function. doNothing函数中有一个奇怪的doNothing We can't let the member thing be the protocol requirement that separates MyValueType from Any , because that member's type is generic — and a protocol with associated-type requirements can't be used as a concrete type (eg as the declaration of a dictionary's element type). 我们不能让成员thing是该协议的要求,即分离MyValueTypeAny ,因为该成员的类型是通用的-与相关型需求的协议不能作为一个具体类型(例如作为字典的声明元素类型)。 Presumably your real MyStruct does something more than just hold a generic thing , so perhaps you can use some other distinctive feature of the type to create a protocol that only it conforms to. 大概您真正的MyStruct所做的不只是持有通用的thing ,因此也许您可以使用该类型的其他一些独特功能来创建仅符合它的协议。

Part 2 第2部分

I guarantee that there'll be a function for every type T will take. 我保证每种T都会有一个函数。

Say that all you want, but the compiler has its fingers in its ears and isn't listening. 说出您想要的所有内容,但是编译器将手指放在耳朵上并且没有监听。 In other words, this is a "guarantee" that you can't enforce in the language, so Swift won't let you do things that proceed from that assumption. 换句话说,这是您不能用该语言强制执行的“保证”,因此Swift不会让您做基于该假设的事情。

Instead, you'll need a dispatch mechanism for reifying the specialization from the generic type through a cast before calling your functions f : 取而代之的是,你需要一个调度机制调用你的函数之前,通过投reifying从通用型专业化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
    }
}

The default case is where your "guarantee" comes into play. 默认情况是您的“担保”发挥作用。 There's no way to make this switch exhaustive as far as the language is concerned, so you need a runtime test to make sure you haven't tried to call f on more types than you claim to have defined. 就语言而言,无法使此switch详尽无遗,因此您需要进行运行时测试,以确保未尝试对超出您声称定义的类型的其他对象调用f

So, indeed, your guess of needing a switch was correct. 因此,确实,您需要switch猜测是正确的。 But it is a good idea, because it (or something similar) is your only way of disambiguating your generic types. 但这一个好主意,因为它(或类似的东西)是消除泛型类型歧义的唯一方法。 You can at least restrict it to needing to handle your MyType protocol instead of Any though. 您至少可以将其限制为需要处理MyType协议,而不是Any

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM