3

假设我们有一个数组,分配给一个类型为Any

let something: Any = ["one", "two", "three"]

我们还假设我们不知道它是一个数组还是完全不同的东西。而且我们也不知道Array.Element我们到底在处理什么样的事情。

现在我们想知道它是否是一个数组。

let isArray = something is Array // compiler error
let isArray = (something as? [Any?] != nil) // does not work (array is [String] and not [Any?])

是否有任何优雅的解决方案可以将以下信息从 swift 类型系统中剔除:

  1. 给定的对象是数组吗
  2. 数组的计数是多少
  3. 给我数组的元素

(桥接到 NSArray 对我来说不是一个解决方案,因为我的数组也可以是 [Any?] 类型并包含零值)

4

5 回答 5

4

我喜欢@stefreak 的问题和他的解决方案。然而,牢记@dfri 对 Swift 运行时自省的出色回答,我们可以在一定程度上简化和概括@stefreak 的“类型标记”方法:

protocol AnySequenceType {
    var anyElements: [Any?] { get }
}

extension AnySequenceType where Self : SequenceType {
    var anyElements: [Any?] {
        return map{
            $0 is NilLiteralConvertible ? Mirror(reflecting: $0).children.first?.value : $0
        }
    }
}

extension Array : AnySequenceType {}
extension Set   : AnySequenceType {}
//    ... Dictionary, etc.

采用:

let things:  Any = [1, 2]
let maybies: Any = [1, nil] as [Int?]

(things  as? AnySequenceType)?.anyElements // [{Some 1}, {Some 2}]
(maybies as? AnySequenceType)?.anyElements // [{Some 1}, nil]

有关允许协议扩展的可能性,请参阅Swift Evolution 邮件列表讨论:

extension<T> Sequence where Element == T?

然而,在目前的实践中,更常见且有些虎头蛇尾的解决方案是:

things as? AnyObject as? [AnyObject] // [1, 2]

// ... which at present (Swift 2.2) passes through `NSArray`, i.e. as if we:

import Foundation
things as? NSArray  // [1, 2]

// ... which is also why this fails for `mabyies`
maybies as? NSArray // nil

无论如何,这一切让我明白的是,一旦你丢失了类型信息,就没有回头路了。即使你反思Mirror你最终还是会得到一个dynamicType,你必须切换到一个预期的类型,这样你就可以转换值并像这样使用它......所有这些都在运行时,永远在编译时检查和理智之外。

于 2016-04-04T19:51:15.010 回答
3

作为@milos 和 OP:s 协议一致性检查的替代方法,我将添加一个使用运行时自省的方法something(在下面foobar示例中)。

/* returns an array if argument is an array, otherwise, nil */
func getAsCleanArray(something: Any) -> [Any]? {
    let mirr = Mirror(reflecting: something)
    var somethingAsArray : [Any] = []
    guard let disp = mirr.displayStyle where disp == .Collection else {
        return nil // not array
    }

    /* OK, is array: add element into a mutable that
     the compiler actually treats as an array */
    for (_, val) in Mirror(reflecting: something).children {
        somethingAsArray.append(val)
    }

    return somethingAsArray
}

示例用法:

/* example usage */
let foo: Any = ["one", 2, "three"]
let bar: [Any?] = ["one", 2, "three", nil, "five"]

if let foobar = getAsCleanArray(foo) {
    print("Count: \(foobar.count)\n--------")
    foobar.forEach { print($0) }
} /* Count: 3
     --------
     one
     2
     three      */

if let foobar = getAsCleanArray(bar) {
    print("Count: \(foobar.count)\n-------------")
    foobar.forEach { print($0) }
} /* Count: 5
     -------------
     Optional("one")
     Optional(2)
     Optional("three")
     nil
     Optional("five")  */
于 2016-04-04T20:12:41.103 回答
1

我想出的唯一解决方案是以下,但我不知道它是否是最优雅的:)

protocol AnyOptional {
    var anyOptionalValue: Optional<Any> { get }
}
extension Optional: AnyOptional {
    var anyOptionalValue: Optional<Any> {
        return self
    }
}
protocol AnyArray {
    var count: Int { get }
    var allElementsAsOptional: [Any?] { get }
}
extension Array: AnyArray {
    var allElementsAsOptional: [Any?] {
        return self.map {
            if let optional = $0 as? AnyOptional {
                return optional.anyOptionalValue
            }
            return $0 as Any?
        }
    }
}

现在你可以说

if let array = something as? AnyArray {
    print(array.count)
    print(array.allElementsAsOptional)
}
于 2016-04-04T19:24:09.810 回答
0

这在操场上对我有用:

// Generate fake data of random stuff
let array: [Any?] = ["one", "two", "three", nil, 1]
// Cast to Any to simulate unknown object received
let something: Any = array as Any

// Use if let to see if we can cast that object into an array
if let newArray = something as? [Any?] {
    // You now know that newArray is your received object cast as an
    // array and can get the count or the elements
} else {
    // Your object is not an array, handle however you need.
}
于 2016-04-04T19:56:12.717 回答
0

我发现强制转换AnyObject适用于一组对象。仍在研究值类型的解决方案。

let something: Any = ["one", "two", "three"]

if let aThing = something as? [Any] {
    print(aThing.dynamicType) // doesn't enter
}

if let aThing = something as? AnyObject {
    if let theThing = aThing as? [AnyObject] {
        print(theThing.dynamicType) // Array<AnyObject>
    }
}
于 2016-04-04T19:58:56.643 回答