Swift中的泛型数组

我一直在玩不同类型的泛型类数组。最简单的解释我的问题与一些示例代码
// 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
    }
}

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

var containers: [Container<MyProtocol>] = []

我得到的错误

Protocol ‘MyProtocol’ can only be used as a generic constraint because it has Self or associated type requirements.

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

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

但是现在在通过容器枚举时出现了另一个“问题”:

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())
    }
}

正如你可以在上面的代码中看到的,在确定容器的类型后,在这两种情况下调用相同的方法。我的问题是:

有没有更好的方法来获得正确的类型的容器,而不是铸造到每一种可能的类型的容器?还是有其他我忽略了?

有一种方式 – 一种 – 做你想要的 – 那种。有一种方法,与协议,消除类型限制,仍然得到你想要的结果,种类,但它并不总是漂亮。这里是我提出了作为一个协议在你的情况:
protocol MyProtocol {
    func getValue() -> Self 
}

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

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

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

这不是很有趣。

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

class Container {
    var values: [MyProtocol]

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

    func myMethod() -> [MyProtocol] {
        return values
    }
}

并且因为Container不再是通用的,你可以创建一个容器数组并迭代它们,打印myMethod()方法的结果:

var containers = [Container]()

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

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

//  Output: [1,6]
//          [1.2,3.5]

诀窍是构造一个包括通用函数的协议,并且没有对符合类型的其他要求。如果你可以逃避这样做,那么你可以使用协议作为一个类型,而不只是作为一个类型约束。

并且作为奖励(如果你想调用它),MyProtocol值的数组甚至可以混合符合MyProtocol的不同类型。所以如果你给String一个MyProtocol扩展,像这样:

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

您可以实际初始化混合类型的容器:

let container = Container(1,4.2,"no kidding,this works")

[警告 – 我在一个在线游乐场测试这个。我还没有能够在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 }
    }
}

现在你仍然可以有一个[ContainerProtocol]对象的数组,并通过调用myMethod()迭代:

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

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

也许仍然不能为你工作,但现在Container限制为一个单一的类型,但你仍然可以迭代通过一个ContainterProtocol对象数组。

相关文章

软件简介:蓝湖辅助工具,减少移动端开发中控件属性的复制和粘...
现实生活中,我们听到的声音都是时间连续的,我们称为这种信...
前言最近在B站上看到一个漂亮的仙女姐姐跳舞视频,循环看了亿...
【Android App】实战项目之仿抖音的短视频分享App(附源码和...
前言这一篇博客应该是我花时间最多的一次了,从2022年1月底至...
因为我既对接过session、cookie,也对接过JWT,今年因为工作...