在swift中键入检查嵌套泛型类

时间:2016-02-02 23:34:13

标签: swift generics

我在swift中解决了以下用例问题,并且不确定它是否可行。

假设我有一个使用泛型的数据结构类 - 让我们调用它     Container<T>

现在我有一个可以是不同类型T的许多Container的列表,例如 Container<A>, Container<B>, Container<A>, Container<C>, Container<C>, ...

我想通过列表,并为每个容器找出&#34;内部类型&#34;是。如果可能的话,我想加入它。

T的可能类型不是有限的,所以我不能对每个类型进行大的开关案例测试。

我尝试的是类似的东西:

if value is Container<???> {      <- 1. should match Container with any type (??? stands here for any type)
   let value = value as! Container<???>   <- 2. a cast to the specific Container
   ...
}

有哪些方法可以解决1.和2.?

3 个答案:

答案 0 :(得分:1)

您可以切换一个值,并有条件地将其转换为某些已知的可能类型(如果案例失败:类型转换成功)。

示例:

class MyClass : CustomStringConvertible {
    var foo : Int = 1
    var description: String { return "MyClass().foo = \(foo)"}
}

struct Container<T> {
    var myVar : T
}

let value1 = Container<Int>(myVar: 1)
let value2 = Container<MyClass>(myVar: MyClass())
let value3 = Container<String>(myVar: "one")
let value4 = "Foobar"

let values : [Any] = [value1, value2, value3, value4]

for (i, value) in values.enumerate() {
    switch value {
    case let foo as Container<Int>:
        print("Element #\(i+1) is an Int container with .myVar = \(foo.myVar)")
    case let foo as Container<String>:
        print("Element #\(i+1) is a String container with .myVar = \(foo.myVar)")
    case let foo as Container<MyClass>:
        print("Element #\(i+1) is a MyClass container with .myVar = \(foo.myVar)")
    case _: print("Element #\(i+1) is of unknown type.")
    }
}

/* Element #1 is an Int container with .myVar = 1
   Element #2 is a MyClass container with .myVar = MyClass().foo = 1
   Element #3 is a String container with .myVar = one                    
   Element #4 is of unknown type.                                      */

注意:就像我发布我的回答一样,我看到了你的编辑补充:

  

“T的可能类型不是有限的,所以我不能有一个大的   为每个人切换案例测试。“

所以你有一个“无限”数量的不同类型可能会保存在你的这个列表中?在这种情况下,我假设您没有自定义操作,具体取决于容器中的内容,在这种情况下,我不明白为什么您需要尝试向下转换(因为,根据您的更新,您可以总是向下倾斜到某些“无限”类型的数据。)

  

“我想通过列表,并为每个容器找出答案   什么是“内在类型”。如果可能的话,我想加入它。“

这相当于只找到容器类型并铸造成自己;后者将永远成功,并没有真正的目的。

因此,剩下的只是找出每个容器的泛型类型的值。你可以这样做,例如使用运行时内省。

设定:

/* Container and protocol setup */
class MyClass : CustomStringConvertible {
    var foo : Int = 1
    var description: String { return "MyClass().foo = \(foo)"}
}

protocol MyUniqueGenericContainer {
    func printMyVar()
}

struct Container<T> : MyUniqueGenericContainer {
    var myVar : T

    // MARK: MyUniqueGenericContainer
    func printMyVar() {
        print(myVar)
    }
}

使用运行时内省查找容器值类型并调用blueprinted方法printMyVar()的示例:

let value1 = Container<Int>(myVar: 1)
let value2 = Container<MyClass>(myVar: MyClass())
let value3 = Container<String>(myVar: "one")
let value4 = "Foobar"

let values : [Any] = [value1, value2, value3, value4]

for (i, value) in values.enumerate() {
    var isKnown = true

    switch value {
    case let val as MyUniqueGenericContainer:
        let valueMirror = Mirror(reflecting: value).children.filter{ $0.label == "myVar" }
        if let containerMyVar = valueMirror.first?.value {
            let containerType = containerMyVar.dynamicType
            print("Element #\(i+1) type: Container<\(containerType)>,",
                ".printMyVar(): ", terminator: "")
            val.printMyVar()
        }
        else { isKnown = false }
    case _ : isKnown = false
    }
    if !isKnown {
        print("Element #\(i+1) not of Container<T> type")
    }
}

打印:

/* Element #1 type: Container<Int>, .printMyVar(): 1
   Element #2 type: Container<MyClass>, .printMyVar(): MyClass().foo = 1
   Element #3 type: Container<String>, .printMyVar(): one
   Element #4 not of Container<T> type                                    */

答案 1 :(得分:0)

使用可选链接:

if let value = value as? ContainerA<XYZ> {
    // value is now of type ContainerA<XYZ>
} else {
    // can't cast
}

答案 2 :(得分:0)

你去......

switch value {
    case is ContainerA<A>:
        ...
    case is ContainerA<B>:
        ...
    case is ContainerA<C>:           
        ...
    default:
        ...
}

if let value = value as? ContainerA<A> {
    // value is now of type ContainerA<A>
} else if let value = value as? ContainerA<B> {
    // value is now of type ContainerA<B>
} else if let value = value as? ContainerA<C> {
    // value is now of type ContainerA<C>
} else {
    // can't cast
}