Swift中的泛型数组

ABa*_*ith 52 arrays generics swift swift-protocols

我一直在玩不同类型的泛型类数组.用一些示例代码解释我的问题最容易:

// Obviously a very pointless protocol...
protocol MyProtocol {
    var value: Self { get }
}

extension Int   : MyProtocol {  var value: Int    { return self } }
extension Double: MyProtocol {  var value: Double { return self } }

class Container<T: MyProtocol> {
    var values: [T]

    init(_ values: T...) {
        self.values = values
    }

    func myMethod() -> [T] {
        return values
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,如果我尝试创建一个像这样的容器数组:

var containers: [Container<MyProtocol>] = []
Run Code Online (Sandbox Code Playgroud)

我收到错误:

协议"MyProtocol"只能用作通用约束,因为它具有Self或关联类型要求.

要解决这个问题,我可以使用[AnyObject]:

let containers: [AnyObject] = [Container<Int>(1, 2, 3), Container<Double>(1.0, 2.0, 3.0)]
// Explicitly stating the types just for clarity.
Run Code Online (Sandbox Code Playgroud)

但是现在列举的另一个"问题"出现了containers:

for container in containers {
    if let c = container as? Container<Int> {
        println(c.myMethod())

    } else if let c = container as? Container<Double> {
        println(c.myMethod())
    }
}
Run Code Online (Sandbox Code Playgroud)

正如您在上面的代码中所看到的,在确定container两种情况下调用相同方法的类型之后.我的问题是:

有没有更好的方法来获得Container正确的类型而不是铸造到每种可能的类型Container?还是有其他我忽略的东西?

Aar*_*sen 50

有一种方式 - 有点 - 做你想做的事 - 有点儿.有一种方法,有协议,消除类型限制,仍然得到你想要的结果,但它并不总是漂亮.以下是我在您的情况下作为协议提出的内容:

protocol MyProtocol {
    func getValue() -> Self 
}

extension Int: MyProtocol {
    func getValue() -> Int {
        return self
    }
}

extension Double: MyProtocol {
    func getValue() -> Double {
        return self
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,value最初放在协议声明中的属性已更改为返回对象的方法.

那不是很有趣.

但是现在,因为你已经摆脱value了协议中的属性,MyProtocol可以用作类型,而不仅仅是类型约束.你的Container班级甚至不再需要通用了.您可以这样声明:

class Container {
    var values: [MyProtocol]

    init(_ values: MyProtocol...) {
        self.values = values
    }

    func myMethod() -> [MyProtocol] {
        return values
    }
}
Run Code Online (Sandbox Code Playgroud)

而且由于Container不再是通用的,你可以创建一个ArrayContainerS和遍历它们,打印结果myMethod()的方法:

var containers = [Container]()

containers.append(Container(1, 4, 6, 2, 6))
containers.append(Container(1.2, 3.5))

for container in containers {
    println(container.myMethod())
}

//  Output: [1, 4, 6, 2, 6]
//          [1.2, 3.5]
Run Code Online (Sandbox Code Playgroud)

诀窍是构建一个仅包含通用函数的协议,并且对符合类型没有其他要求.如果您可以逃脱这样做,那么您可以将协议用作类型,而不仅仅是类型约束.

作为奖励(如果你想称之为),你的数组MyProtocol值甚至可以混合符合的不同类型MyProtocol.所以如果你给这样StringMyProtocol扩展:

extension String: MyProtocol {
    func getValue() -> String {
        return self
    }
}
Run Code Online (Sandbox Code Playgroud)

您实际上可以Container使用混合类型初始化a :

let container = Container(1, 4.2, "no kidding, this works")
Run Code Online (Sandbox Code Playgroud)

[警告 - 我正在其中一个在线游乐场进行测试.我还没能在Xcode中测试它...]

编辑:

如果你仍然想Container成为通用的,只能容纳一个类型的对象,就可以完成,通过使符合自己的协议:

protocol ContainerProtocol {
    func myMethod() -> [MyProtocol]
}

class Container<T: MyProtocol>: ContainerProtocol {
    var values: [T] = []

    init(_ values: T...) {
        self.values = values
    } 

    func myMethod() -> [MyProtocol] {
        return values.map { $0 as MyProtocol }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在你仍然可以拥有一个[ContainerProtocol]对象数组并迭代它们调用myMethod():

let containers: [ContainerProtocol] = [Container(5, 3, 7), Container(1.2, 4,5)]

for container in containers {
    println(container.myMethod())
}
Run Code Online (Sandbox Code Playgroud)

也许这仍然不适合你,但现在Container只限于一种类型,但你仍然可以遍历一个ContainterProtocol对象数组.


Rob*_*ier 8

这是"你想要发生什么?"的一个很好的例子.并且实际上展示了如果Swift真的具有一流类型而爆炸的复杂性.

protocol MyProtocol {
    var value: Self { get }
}
Run Code Online (Sandbox Code Playgroud)

大.MyProtocol.value返回任何类型实现它,记住这必须在编译时确定,而不是运行时.

var containers: [Container<MyProtocol>] = []
Run Code Online (Sandbox Code Playgroud)

那么,在编译时确定,这是什么类型的?忘记编译器,只需在纸上完成.是的,不知道会是什么类型.我的意思是具体类型.没有元型.

let containers: [AnyObject] = [Container<Int>(1, 2, 3), Container<Double>(1.0, 2.0, 3.0)]
Run Code Online (Sandbox Code Playgroud)

你知道,当AnyObject你偷偷进入你的签名时,你走错了路.关于这一点的任何事情都无法发挥作用.之后AnyObject只是麻布.

还是有其他我忽略的东西?

是.你需要一个类型,但你没有提供.您提供了约束类型的规则,但没有实际类型.回到你真正的问题,并更深入地思考它.(元数据分析几乎不是你的"真正的"问题,除非你正在攻读CS博士,在这种情况下你会在伊德里斯做这个,而不是斯威夫特.)你解决了什么实际问题?