Swift:数组属性,元素同时符合class和多个协议
Swift: Array property with elements conforming to a class and multiple protocols simultaneously
在 Objective-C 中有一种方法可以声明符合 class 和一组协议的变量,如下所示:
BaseClass<Protocol1, Protocol2> *variable = ...
在 Swift 中,我想声明一个数组(实际上是 class 的 属性),其元素类型由此模式定义。
在 this question 中,有一个解决方案可以通过使 class 通用并相应地限制类型来描述独立 属性 的类型。为了实例化这样的 class ,有必要指定确切的类型。这对于独立 属性 来说不是问题,但在数组中应该可以存储具有不同确切类型的元素。
有没有办法在Swift中表达这个?
在这种情况下,您只想存储符合您的协议的对象,您可以创建另一个继承其他协议的协议,例如
protocol A { }
protocol B { }
protocol C : A, B { }
现在可以创建对应的数组了
var objects : [ C ]
您可以存储任何对象,只要它符合 C
-协议,因此也符合 A
和 B
:
class Foo : X { }
class Bar : X { }
objects.append(Foo()) // [ Foo ]
objects.append(Bar()) // [ Foo, Bar ]
背后的技术是Protocol Inheritance。
更新 IMO 这对于 Swift 的 Array
是不可行的。因为,您可以存储从基础 class 或 AnyObject
继承的类型,这不满足您的约束。但是您可以创建一个包装器来检查您尝试附加到数组的对象,如果它不符合约束则拒绝它。
感谢 @SebastianDressler and @Mike-S 我发现在 Swift 中没有直接的表达方式。
不过,您可以像这样限制要添加到数组中的项目的类型:
func addItem<T where T: BaseClass, T: Protocol1, T: Protocol2>(item: T) {
self.array.append(item)
}
当数组的类型被定义为协议的基 class 之一时,从数组中转换项目是不可能的,因为编译器看不到这些类型之间的任何关系。
为了能够转换为基础 class 或协议之一,有必要将类型声明为 AnyObject
。
var array: [AnyObject] = []
并且仅当使用 @objc
注释时,转换为协议才有效(参见 )。
我刚遇到这个老问题,因为 Swift 语言在接受部分答案后发生了变化,所以我决定 post 另一个实际上解决了我最初提出的问题的答案。
从版本 4 开始,Swift 支持使用 &
符号组合协议,也可以使用一种 class 类型组合。
class BaseClass {}
protocol Protocol1 {}
protocol Protocol2 {}
class ConformingClass1: BaseClass, Protocol1, Protocol2 {}
class ConformingClass2: BaseClass, Protocol1, Protocol2 {}
// It works for a variable holding a single object.
let object: BaseClass & Protocol1 & Protocol2 = ConformingClass1()
// And it also works for a variable holding an array of objects.
let array: [BaseClass & Protocol1 & Protocol2] = [ConformingClass1(), ConformingClass2()]
在 Objective-C 中有一种方法可以声明符合 class 和一组协议的变量,如下所示:
BaseClass<Protocol1, Protocol2> *variable = ...
在 Swift 中,我想声明一个数组(实际上是 class 的 属性),其元素类型由此模式定义。
在 this question 中,有一个解决方案可以通过使 class 通用并相应地限制类型来描述独立 属性 的类型。为了实例化这样的 class ,有必要指定确切的类型。这对于独立 属性 来说不是问题,但在数组中应该可以存储具有不同确切类型的元素。
有没有办法在Swift中表达这个?
在这种情况下,您只想存储符合您的协议的对象,您可以创建另一个继承其他协议的协议,例如
protocol A { }
protocol B { }
protocol C : A, B { }
现在可以创建对应的数组了
var objects : [ C ]
您可以存储任何对象,只要它符合 C
-协议,因此也符合 A
和 B
:
class Foo : X { }
class Bar : X { }
objects.append(Foo()) // [ Foo ]
objects.append(Bar()) // [ Foo, Bar ]
背后的技术是Protocol Inheritance。
更新 IMO 这对于 Swift 的 Array
是不可行的。因为,您可以存储从基础 class 或 AnyObject
继承的类型,这不满足您的约束。但是您可以创建一个包装器来检查您尝试附加到数组的对象,如果它不符合约束则拒绝它。
感谢 @SebastianDressler and @Mike-S 我发现在 Swift 中没有直接的表达方式。
不过,您可以像这样限制要添加到数组中的项目的类型:
func addItem<T where T: BaseClass, T: Protocol1, T: Protocol2>(item: T) {
self.array.append(item)
}
当数组的类型被定义为协议的基 class 之一时,从数组中转换项目是不可能的,因为编译器看不到这些类型之间的任何关系。
为了能够转换为基础 class 或协议之一,有必要将类型声明为 AnyObject
。
var array: [AnyObject] = []
并且仅当使用 @objc
注释时,转换为协议才有效(参见 )。
我刚遇到这个老问题,因为 Swift 语言在接受部分答案后发生了变化,所以我决定 post 另一个实际上解决了我最初提出的问题的答案。
从版本 4 开始,Swift 支持使用 &
符号组合协议,也可以使用一种 class 类型组合。
class BaseClass {}
protocol Protocol1 {}
protocol Protocol2 {}
class ConformingClass1: BaseClass, Protocol1, Protocol2 {}
class ConformingClass2: BaseClass, Protocol1, Protocol2 {}
// It works for a variable holding a single object.
let object: BaseClass & Protocol1 & Protocol2 = ConformingClass1()
// And it also works for a variable holding an array of objects.
let array: [BaseClass & Protocol1 & Protocol2] = [ConformingClass1(), ConformingClass2()]