Swift 阵列上的模式匹配<Any>
Swift Pattern match on Array<Any>
Swift 1.2
我正在尝试在一个函数中的 switch case 中进行模式匹配,该函数采用 Any 类型作为参数,以便分派给私有的更专业的 init。
这是一个 Playground 推断:
import Foundation
struct myStruct {
}
func switchOnAny(any: Any) -> String {
println("Dynamic Type == \(any.dynamicType)")
switch any {
case let array as [Any]:
return "Array"
case let array as NSArray:
return "NSArray"
default:
return "Default"
}
}
let emptyStringArray : [String] = []
let stringArray : [String] = ["Bob", "Roger"]
let intArray = [1, 2, 3]
let customStructArray : [myStruct] = []
println("\t\touput : \(switchOnAny([]))")
println("\t\touput : \(switchOnAny(emptyStringArray))")
println("\t\touput : \(switchOnAny(stringArray))")
println("\t\touput : \(switchOnAny(intArray))")
println("\t\touput : \(switchOnAny(customStructArray))")
产生以下输出:
Dynamic Type == __NSArrayI
ouput : NSArray
Dynamic Type == Swift.Array
ouput : NSArray
Dynamic Type == Swift.Array
ouput : NSArray
Dynamic Type == Swift.Array
ouput : NSArray
Dynamic Type == Swift.Array<__lldb_expr_37.myStruct>
ouput : Default
我想知道为什么 as [Any]
没有得到它,因为我从不请求 NSArray?
我是否可以假设任何类型的 Swift 数组都会出现在 NSArray 案例中,或者我是否需要编写 2 个案例语句(一个用于 NSArray,一个用于 [Any])以涵盖我的回来(貌似会有需求)?
在进行更多测试后,我发现当我提供一个自定义结构数组时,none 模式将匹配。我将需要像 [myStruct] 这样的匹配才能识别。这正是我要避免的,因为它只是我可以接受的选项之一。
为了提供更多背景信息,我将我的项目放在 Github : https://github.com/VinceBurn/SwiftyPlist/tree/test/init-Any 上。
该项目是关于 TDD 并将 属性 列表表示为可以通过下标访问的类似树的结构。 (比如 SwiftyJSON)
不幸的是,Array
之类的泛型类型之间的转换还没有得到完全支持(目前)。即使想upcast也有奇怪的情况:
let emptyStringArray : [String] = []
emptyStringArray as [Any] // succeeds
let stringArray : [String] = ["Bob", "Roger"]
stringArray as [Any] // error! due to the elements?!
let intArray = [1, 2, 3]
intArray as [Any] // error
let customStructArray : [myStruct] = []
customStructArray as [Any] // '[myStruct]' is not convertible to '[Any]'
不使用协议也没有好的解决方法。如果你真的想要这种动态行为,你可以将反射与 reflect()
函数结合使用。在 Swift 2 中它们更强大,但仍然不是一个好的解决方案。
编辑:
一个解决方案,其协议通过扩展被所有人 Arrays
采用(仅适用于您的特定情况):
protocol ArrayType {
var anyValues: [Any] { get }
}
extension Array: ArrayType {
var anyValues: [Any] {
return self.map { [=11=] as Any }
}
}
// now the switch gets rewritten as
switch any {
case let array as ArrayType:
let anyArray = array.anyValues
return "Array"
case let array as NSArray:
return "NSArray"
default:
return "Default"
}
最可靠地判断一个变量是否是任何类型的数组是使用反射,在Swift 1.2:
let array = []
let mirror = reflect(array)
let isArray = mirror.disposition == MirrorDisposition.IndexContainer
和 Swift 2.0:
let anArray = []
let mirror = Mirror(reflecting: anArray)
let isArray = mirror.displayStyle == .Collection
出于好奇,查看这些枚举很有趣:
enum MirrorDisposition { //Swift 1.2
case Struct
case Class
case Enum
case Tuple
case Aggregate
case IndexContainer
case KeyContainer
case MembershipContainer
case Container
case Optional
case ObjCObject
}
enum DisplayStyle { //Swift 2.0
case Struct
case Class
case Enum
case Tuple
case Optional
case Collection
case Dictionary
case Set
}
已更新
这是一个完整的模式匹配示例:
func switchOnAny(any: Any) -> String {
println("Dynamic Type == \(any.dynamicType)")
switch any {
case let array as Any where reflect(any).disposition == MirrorDisposition.IndexContainer:
return "Any kind of array"
default:
return "Default"
}
}
我建议使用自定义协议扩展 Array
类型,您可以像这样检查它:
protocol ArrayType {}
extension Array : ArrayType {}
那么你可以这样做:
let array : Any = [1, 2, 3, 4]
array is ArrayType // true
也可以看看我的另一个回答。
但实际上看起来您不想拥有一个以 Any
作为参数的 public 初始值设定项(您很少想要它),而是两个不同的初始值设定项,一种用于数组,一种用于非数组,如下所示:
class MyClass {
init<T>(array: [T]) {
}
init<T>(nonArray: T) {
}
}
结论:在Swift 1.2
通过 Kametrixom 和 Daniel Nagy 的回答,可以为所有类型的数组输入一个开关盒。
但是在案例中,我无法将项目转换为适用于所有案例的可用数组。
所以最后我留下了 2 个案例陈述,一个
case let array as NSArray:
return "NSArray"
case let array as [myStruct]:
return "myStruct array"
Swift 1.2
我正在尝试在一个函数中的 switch case 中进行模式匹配,该函数采用 Any 类型作为参数,以便分派给私有的更专业的 init。
这是一个 Playground 推断:
import Foundation
struct myStruct {
}
func switchOnAny(any: Any) -> String {
println("Dynamic Type == \(any.dynamicType)")
switch any {
case let array as [Any]:
return "Array"
case let array as NSArray:
return "NSArray"
default:
return "Default"
}
}
let emptyStringArray : [String] = []
let stringArray : [String] = ["Bob", "Roger"]
let intArray = [1, 2, 3]
let customStructArray : [myStruct] = []
println("\t\touput : \(switchOnAny([]))")
println("\t\touput : \(switchOnAny(emptyStringArray))")
println("\t\touput : \(switchOnAny(stringArray))")
println("\t\touput : \(switchOnAny(intArray))")
println("\t\touput : \(switchOnAny(customStructArray))")
产生以下输出:
Dynamic Type == __NSArrayI
ouput : NSArray
Dynamic Type == Swift.Array
ouput : NSArray
Dynamic Type == Swift.Array
ouput : NSArray
Dynamic Type == Swift.Array
ouput : NSArray
Dynamic Type == Swift.Array<__lldb_expr_37.myStruct>
ouput : Default
我想知道为什么 as [Any]
没有得到它,因为我从不请求 NSArray?
我是否可以假设任何类型的 Swift 数组都会出现在 NSArray 案例中,或者我是否需要编写 2 个案例语句(一个用于 NSArray,一个用于 [Any])以涵盖我的回来(貌似会有需求)?
在进行更多测试后,我发现当我提供一个自定义结构数组时,none 模式将匹配。我将需要像 [myStruct] 这样的匹配才能识别。这正是我要避免的,因为它只是我可以接受的选项之一。
为了提供更多背景信息,我将我的项目放在 Github : https://github.com/VinceBurn/SwiftyPlist/tree/test/init-Any 上。 该项目是关于 TDD 并将 属性 列表表示为可以通过下标访问的类似树的结构。 (比如 SwiftyJSON)
不幸的是,Array
之类的泛型类型之间的转换还没有得到完全支持(目前)。即使想upcast也有奇怪的情况:
let emptyStringArray : [String] = []
emptyStringArray as [Any] // succeeds
let stringArray : [String] = ["Bob", "Roger"]
stringArray as [Any] // error! due to the elements?!
let intArray = [1, 2, 3]
intArray as [Any] // error
let customStructArray : [myStruct] = []
customStructArray as [Any] // '[myStruct]' is not convertible to '[Any]'
不使用协议也没有好的解决方法。如果你真的想要这种动态行为,你可以将反射与 reflect()
函数结合使用。在 Swift 2 中它们更强大,但仍然不是一个好的解决方案。
编辑:
一个解决方案,其协议通过扩展被所有人 Arrays
采用(仅适用于您的特定情况):
protocol ArrayType {
var anyValues: [Any] { get }
}
extension Array: ArrayType {
var anyValues: [Any] {
return self.map { [=11=] as Any }
}
}
// now the switch gets rewritten as
switch any {
case let array as ArrayType:
let anyArray = array.anyValues
return "Array"
case let array as NSArray:
return "NSArray"
default:
return "Default"
}
最可靠地判断一个变量是否是任何类型的数组是使用反射,在Swift 1.2:
let array = []
let mirror = reflect(array)
let isArray = mirror.disposition == MirrorDisposition.IndexContainer
和 Swift 2.0:
let anArray = []
let mirror = Mirror(reflecting: anArray)
let isArray = mirror.displayStyle == .Collection
出于好奇,查看这些枚举很有趣:
enum MirrorDisposition { //Swift 1.2
case Struct
case Class
case Enum
case Tuple
case Aggregate
case IndexContainer
case KeyContainer
case MembershipContainer
case Container
case Optional
case ObjCObject
}
enum DisplayStyle { //Swift 2.0
case Struct
case Class
case Enum
case Tuple
case Optional
case Collection
case Dictionary
case Set
}
已更新 这是一个完整的模式匹配示例:
func switchOnAny(any: Any) -> String {
println("Dynamic Type == \(any.dynamicType)")
switch any {
case let array as Any where reflect(any).disposition == MirrorDisposition.IndexContainer:
return "Any kind of array"
default:
return "Default"
}
}
我建议使用自定义协议扩展 Array
类型,您可以像这样检查它:
protocol ArrayType {}
extension Array : ArrayType {}
那么你可以这样做:
let array : Any = [1, 2, 3, 4]
array is ArrayType // true
也可以看看我的另一个回答
但实际上看起来您不想拥有一个以 Any
作为参数的 public 初始值设定项(您很少想要它),而是两个不同的初始值设定项,一种用于数组,一种用于非数组,如下所示:
class MyClass {
init<T>(array: [T]) {
}
init<T>(nonArray: T) {
}
}
结论:在Swift 1.2
通过 Kametrixom 和 Daniel Nagy 的回答,可以为所有类型的数组输入一个开关盒。
但是在案例中,我无法将项目转换为适用于所有案例的可用数组。
所以最后我留下了 2 个案例陈述,一个
case let array as NSArray:
return "NSArray"
case let array as [myStruct]:
return "myStruct array"