假设我们有一个数组,分配给类型为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类型系统中获取以下信息:
(桥接到NSArray不是我的解决方案,因为我的数组也可以是[Any?]类型并且包含nil-values)
答案 0 :(得分: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 mailing list discussion关于允许协议扩展的可能性:
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
,你必须切换到一个期望的类型,这样你就可以转换它并使用它...所有在运行时,所有永远在编译时间之外检查和理智。
答案 1 :(得分:3)
作为@milos和OP:s协议一致性检查的替代方法,我将使用something
(foo
和bar
的运行时内省添加一个方法,如下例所示)
/* 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") */
答案 2 :(得分: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)
}
答案 3 :(得分: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.
}
答案 4 :(得分: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>
}
}