数组上的Swift模式匹配<any>

时间:2015-08-22 13:53:12

标签: swift switch-statement pattern-matching swift1-2

Swift 1.2
我正在尝试在一个函数中切换案例中的匹配,该函数采用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))")

产生以下输出:

  

动态类型== __NSArrayI
          产量:NSArray
  动态类型== Swift.Array
          产量:NSArray
  动态类型== Swift.Array
          产量:NSArray
  动态类型== Swift.Array
          产量:NSArray
  动态类型== Swift.Array&lt; __ lldb_expr_37.myStruct&gt;

          输出:默认

我想知道为什么案例as [Any]没有得到它,因为我从未请求过NSArray?

我可以假设任何类型的Swift数组都会进入NSArray案例,或者我需要编写2个case语句(一个用于NSArray,一个用于[Any])以覆盖我的背部(显然会有需要)?

在进行了一些测试之后,我可以看到,当我提供一个自定义结构的数组时,没有任何模式匹配。我需要像[myStruct]这样的匹配才能识别。这正是我想要避免的,因为它只是我可以接受的选项之一。

为了提供更多上下文,我将我的项目放在Github上:https://github.com/VinceBurn/SwiftyPlist/tree/test/init-Any。 该项目是关于TDD并将属性列表表示为可以通过下标访问的类似树的结构树。 (比如SwiftyJSON)

4 个答案:

答案 0 :(得分:3)

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"
    }
}

答案 1 :(得分:2)

不幸的是,尚未完全支持像Array这样的泛型类型之间的转换。即使你想要上传也有奇怪的情况:

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 { $0 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"
}

答案 2 :(得分:1)

我建议使用自定义协议扩展Array类型,您可以使用它来检查它:

protocol ArrayType {}
extension Array : ArrayType {}

然后你可以这样做:

let array : Any = [1, 2, 3, 4]
array is ArrayType    // true

另请查看我的其他答案here

但实际上你看起来并不想拥有一个以Any作为参数的公共初始化器(你很少想要它),而是两个不同的初始化器,一个用于数组,一个非数组,如:

class MyClass {
    init<T>(array: [T]) {

    }

    init<T>(nonArray: T) {

    }
}

答案 3 :(得分:0)

结论:在Swift 1.2中

在Kametrixom和Daniel Nagy回答的情况下,可以为所有类型的阵列输入一个开关盒。
但在这种情况下,我无法将项目强制转换为可用的数组。

总而言之,我留下了2个案例陈述,一个

case let array as NSArray:
    return "NSArray"
case let array as [myStruct]:
    return "myStruct array"