Swift 获取未知类型数组的元素和计数
假设我们有一个数组,分配给类型为Swift 获取未知类型数组的元素和计数,swift,swift2.2,Swift,Swift2.2,假设我们有一个数组,分配给类型为Any let something: Any = ["one", "two", "three"] 让我们也假设我们不知道它是一个数组还是完全其他的东西。我们也不知道我们要处理的是什么类型的数组.Element 现在我们想知道它是否是一个数组 let isArray = something is Array // compiler error let isArray = (something as? [Any?] != nil) // does not work
Any
let something: Any = ["one", "two", "three"]
让我们也假设我们不知道它是一个数组还是完全其他的东西。我们也不知道我们要处理的是什么类型的数组.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?]类型,并且包含零值)我提出的唯一解决方案是以下解决方案,但我不知道它是否是最优雅的:)
我喜欢@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.
使用:
关于允许协议扩展的可能性,请参见:
extension<T> Sequence where Element == T?
无论如何,所有这些让我明白的是,一旦你丢失了类型信息,就再也回不去了。即使您在
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>
}
}
Any=[“一”、“二”、“三”]
如果让aThing=某物作为?[任何]{
打印(aThing.dynamicType)//不输入
}
如果让aThing=某物作为?任何对象{
如果让thing=aThing为?[任何对象]{
打印(theThing.dynamicType)//数组
}
}
作为@milos和OP:s协议一致性检查的替代方法,我将添加一个方法,使用某物的运行时内省(foo
和bar
,在下面的示例中)
用法示例:
/* 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") */
关键是,在运行时,它可以是任何东西的数组。我们如何才能转换某物
,所以我们实际上有了变量数组
?如果你已经知道它是一个数组,为什么不这样分配它呢让某物:[Any?]=[…]
?@EmilioPelaez实际上我从其他地方得到数组(作为另一个[Any]
数组的元素之一),所以上面的任务是一个简化,所以更容易解释这个问题。扩展数组:P{};something is P
您还可以使用类型内省来查看something
是否具有集合显示样式,例如,如果让disp=Mirror(反射:something)。displaystyle其中disp==.collection{//is array},但是我相信上面的@milos协议一致性检查是理想的。我个人认为这不是一个问题,因为AnyArray
协议不应该被随机结构和类类型所遵循:它应该只存在(我们应该能够让声明它的开发人员承担这么多责任)作为阵列
的类型测试协议。因此,将上面的Dummy
与AnyArray
一致,imho,实际上并没有涵盖我们可能遇到的实际情况(即使有点偏离“逻辑错误安全”并将其交给开发人员……)。非常感谢!)问题/区别到底是什么?另外,您非常好的三行解决方案只解决了我问题的第一部分(给定对象是数组吗),而不是第二部分。和3.@stefreak我相信milos在这里指出的是,当我们仅创建此协议用于检查某个对象是否是数组时,可以对某些结构/类/etc类型使用协议AnyProtocol
(符合它)。如果有人像这样使用AnyArray
,它会在其他地方破坏我们的类型控制。因此,它确实描述了一个可能的问题,但是一个开发人员在逻辑错误中出错的问题。只要你在运行一个人的项目,你就很可能很安全,因为你知道自己永远不会让随机类型符合AnyArray
。是的,很好地说@dfri。但我也是+1 stefreak的解决方案。Q和A都很好。@milos在你的最后一部分:只有在你的类型完全正确的情况下,条件转换才有效。因此,如果您将让maybies:Any=[1,nil]写为[Int?]
并且让things:Any=[1,2]写为[Int]
它将停止工作,因为您已经显式地将数组
声明为数组(可选的Any
:s)。OP描述的问题是,我们只知道某物
可以通过类型任何
捕获(即,可以是任何东西),并且某物
不一定是数组。如果我们可以首先将某物
声明为数组,那么这里就不会有任何问题。嗯,如果您将第一行替换为let array:[字符串?]=[“一”,“二”,“三”,零]
@dfri我确实将数组
声明为数组,因为let something:any=[…]
不编译,正如你在答案中所看到的,数组
只被用来强制转换为任何
对象。@EmilioPelaez让一些东西:Any=[“一”、“二”、“三”]
用Xcode 7.3/Swift 2.2为我编译。@JAL Try让数组:[Any?]=[“一”、“二”、“三”,nil,1]
,我试图模拟比“三个字符串”更随机的数据,但当某个东西是一个可选的数组时,它似乎不再起作用:(让某个东西:Any=[“一”、“二”、“三”,nil]作为[String?]
@stefreak,这是因为Any
比optional
更“可选”。
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
// 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.
}
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>
}
}
/* 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") */