在泛型中使用类型变量

时间:2015-06-18 01:25:19

标签: swift generics

除了Swift,我有this question。如何在通用中使用Type变量?

我试过了:

func intType() -> Int.Type {
    return Int.self
}

func test() {
    var t = self.intType()
    var arr = Array<t>() // Error: "'t' is not a type". Uh... yeah, it is.
}

这也不起作用:

var arr = Array<t.Type>() // Error: "'t' is not a type"
var arr = Array<t.self>() // Swift doesn't seem to even understand this syntax at all.

有办法做到这一点吗?我觉得Swift不支持它并且给我一些含糊不清的错误消息。

编辑:这是一个更复杂的示例,使用通用函数标头无法规避问题。当然它没有意义,但我在我的代码中的某个地方有一个明智的用途,而不是发布一个干净的例子而不是我的实际代码:

func someTypes() -> [Any.Type] {
    var ret = [Any.Type]()
    for (var i = 0; i<rand()%10; i++) {
        if (rand()%2 == 0){ ret.append(Int.self) }
        else {ret.append(String.self) }
    }
    return ret
}

func test() {
    var ts = self.someTypes()

    for t in ts {
        var arr = Array<t>()
    }
}

5 个答案:

答案 0 :(得分:26)

Swift的static typing表示必须在编译时知道变量的类型。

在泛型函数func foo<T>() { ... }的上下文中, T 看起来像一个变量,但其类型实际上是在编译时根据函数从< / em>的。 Array<T>()的行为取决于T,但此信息 在编译时已知。

使用协议时,Swift使用动态调度,因此您可以编写Array<MyProtocol>(),并且数组只存储对实现MyProtocol的内容的引用 - 所以当您获得某些内容时在数组之外,您可以访问MyProtocol所需的所有函数/变量/类型。

但如果t实际上是类Any.Type变量,则Array<t>()没有意义,因为它的类型在编译时实际上是未知的。 (由于Array是一个通用结构,编译器需要知道哪个类型用作泛型参数,但这是不可能的。)

我建议今年观看WWDC的一些视频:

我发现这张幻灯片特别有助于理解协议和动态调度:

答案 1 :(得分:6)

有一种方法,它被称为泛型。你可以这样做。

class func foo() {
    test(Int.self)
}

class func test<T>(t: T.Type) {
    var arr = Array<T>()
}

您需要以某种方式提示您想要专门化该函数的类型的编译器。另一种方法是使用return param(在这种情况下被丢弃):

class func foo() {
    let _:Int = test()
}

class func test<T>() -> T {
    var arr = Array<T>()
}

在类(或结构)上使用泛型,你不需要额外的参数:

class Whatever<T> {
    var array = [T]() // another way to init the array.
}

let we = Whatever<Int>()

答案 2 :(得分:4)

jtbandes&#39;回答 - 您无法使用当前的方法,因为Swift是静态类型的 - 是正确的。

但是,如果您愿意在数组中创建允许类型的白名单,例如在funcYgivenX中,则可以在运行时动态初始化不同类型。

首先,创建一个允许类型的enum

enum

创建enum Types { case Int case String } 课程。实现Example函数以使用这些枚举值。 (您可以轻松地将JSON字符串数组转换为此枚举的数组。)

someTypes()

现在使用class Example { func someTypes() -> [Types] { var ret = [Types]() for _ in 1...rand()%10 { if (rand()%2 == 0){ ret.append(.Int) } else {ret.append(.String) } } return ret } 为每个允许类型范围switch实现测试功能:

arr

正如您所知,您也可以将 func test() { let types = self.someTypes() for type in types { switch type { case .Int: var arr = [Int]() arr += [4] case .String: var arr = [String]() arr += ["hi"] } } } } 声明为arr以混合类型(&#34;异性&#34;案例在jtbandes&#39;答案中):

[Any]

答案 3 :(得分:3)

我会用你从第一个答案中学到的东西来分解它。我冒昧地重构了一些代码。这是:

func someTypes<T>(t: T.Type) -> [Any.Type] {
    var ret = [Any.Type]()
    for _ in 0..<rand()%10 {
        if (rand()%2 == 0){ ret.append(T.self) }
        else {
            ret.append(String.self)
        }
    }
    return ret
}


func makeArray<T>(t: T) -> [T] {
    return [T]()
}

func test() {
    let ts = someTypes(Int.self)
    for t in ts {
        print(t)
    }
}

这有点奏效,但我相信这样做非常不正统。你能用反射(镜像)吗?

答案 4 :(得分:0)

有可能,只要您可以向编译器提供有关... T类型的“提示”。因此,在下面的示例中,必须使用: String?

func cast<T>(_ value: Any) -> T? {
    return value as? T
}

let inputValue: Any = "this is a test"
let casted: String? = cast(inputValue) 
print(casted) // Optional("this is a test")
print(type(of: casted)) // Optional<String>

为什么Swift不仅允许我们let casted = cast<String>(inputValue)我永远不会知道。


一个烦人的场景是当您的函数没有返回值时。然后,它不一定总是提供必要的“提示”。让我们来看这个例子...

func asyncCast<T>(_ value: Any, completion: (T?) -> Void) {
    completion(value as? T)
}

以下客户端代码未编译。它给出了“无法推断通用参数'T'”错误。

let inputValue: Any = "this is a test"
asyncCast(inputValue) { casted in
    print(casted) 
    print(type(of: casted))
}

但是您可以通过向编译器提供“提示”来解决此问题,如下所示:

asyncCast(inputValue) { (casted: String?) in
    print(casted) // Optional("this is a test")
    print(type(of: casted)) // Optional<String>
}