获取未知类型数组的元素和计数
Get elements and count of Array of unknown type
假设我们有一个数组,分配给类型为 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 值)
我想到的唯一解决方案如下,但我不知道它是否是最优雅的:)
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 = [=10=] as? AnyOptional {
return optional.anyOptionalValue
}
return [=10=] as Any?
}
}
}
现在你可以说
if let array = something as? AnyArray {
print(array.count)
print(array.allElementsAsOptional)
}
我喜欢@stefreak 的问题和他的解决方案。牢记@dfri 关于 Swift 运行时自省的出色回答,但是,我们可以在某种程度上简化和概括@stefreak 的 "type tagging" 方法:
protocol AnySequenceType {
var anyElements: [Any?] { get }
}
extension AnySequenceType where Self : SequenceType {
var anyElements: [Any?] {
return map{
[=10=] is NilLiteralConvertible ? Mirror(reflecting: [=10=]).children.first?.value : [=10=]
}
}
}
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
,您必须将其切换到预期的类型,以便您可以转换该值并按原样使用它......所有在运行时,所有永远在编译时检查和完整性之外。
这对我来说适用于游乐场:
// 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.
}
我发现转换为 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>
}
}
作为@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([=11=]) }
} /* Count: 3
--------
one
2
three */
if let foobar = getAsCleanArray(bar) {
print("Count: \(foobar.count)\n-------------")
foobar.forEach { print([=11=]) }
} /* Count: 5
-------------
Optional("one")
Optional(2)
Optional("three")
nil
Optional("five") */
假设我们有一个数组,分配给类型为 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 值)
我想到的唯一解决方案如下,但我不知道它是否是最优雅的:)
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 = [=10=] as? AnyOptional {
return optional.anyOptionalValue
}
return [=10=] as Any?
}
}
}
现在你可以说
if let array = something as? AnyArray {
print(array.count)
print(array.allElementsAsOptional)
}
我喜欢@stefreak 的问题和他的解决方案。牢记@dfri 关于 Swift 运行时自省的出色回答,但是,我们可以在某种程度上简化和概括@stefreak 的 "type tagging" 方法:
protocol AnySequenceType {
var anyElements: [Any?] { get }
}
extension AnySequenceType where Self : SequenceType {
var anyElements: [Any?] {
return map{
[=10=] is NilLiteralConvertible ? Mirror(reflecting: [=10=]).children.first?.value : [=10=]
}
}
}
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
,您必须将其切换到预期的类型,以便您可以转换该值并按原样使用它......所有在运行时,所有永远在编译时检查和完整性之外。
这对我来说适用于游乐场:
// 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.
}
我发现转换为 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>
}
}
作为@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([=11=]) }
} /* Count: 3
--------
one
2
three */
if let foobar = getAsCleanArray(bar) {
print("Count: \(foobar.count)\n-------------")
foobar.forEach { print([=11=]) }
} /* Count: 5
-------------
Optional("one")
Optional(2)
Optional("three")
nil
Optional("five") */