如何判断一个变量是否为数组

13

我有一个 Swift 函数,它可以接受任何类型的参数,并且我希望它能够接受字符串数组、整数数组、混合数组或者嵌套数组等等。同时,该函数也可以接受单个字符串或整数等非数组形式的参数。

所以我的代码是这样的:

    private func parse(parameter: Any) {
        if parameter is Int {
            // Int
        } else if (parameter is Float) || (parameter is Double) {
            // Double
        } else if parameter is String {
            // String
        } else if parameter is Bool {
            // Bool
        } else if let array = parameter as? [Any] {
            // Should catch all Arrays
        } else {
            assert(false, "Unsupported type") // [String] ends up here
        }
    }

但是如果我调用parse(["Strings"]),就会触发断言。我该如何捕获所有类型的数组?

编辑 - 有些混淆了我的目标。基本上,我需要根据类型返回一个字符串,所以 Int -> "" 和 String -> "",因此数组将进行递归调用以返回 "..."。

此帖子被标记为重复,但那个问题是关于Javascript而不是Swift的。


user3352495为false,对[任何]的检查仍然不能按预期工作(Swift 5) - Jerem Lachkar
4个回答

13

我最终找到了实现该操作的方法,那就是使用NSArray进行强制类型转换。

private func parse(x: Any) {
    if let o = x as? [Any] {
        println("[Any]")
    }
    if let o = x as? [AnyObject] {
        println("[AnyObject]")
    }
    if let o = x as? NSArray {
        println("NSArray")
    }
}

let a: [Any] = ["bar"]
let b: [AnyObject] = ["bar"]
let c = ["foo", 3.14]

parse(a) // ==> [Any]
parse(b) // ==> [AnyObject], and also NSArray
parse(c) // ==> NSArray

看起来是一个包含Any类型值的数组,在内部使用NSArray表示。 (但如果将c强制转换为[Any],是否应该可以呢?我怀疑这是一个bug。)


给我一分钟尝试在我的实现中实现这个想法! - Ryan
哇!这个可行!我会继续尝试简化以便向雷达展示一个好的例子,但在此期间我会使用这个。 - Ryan
Swift 5看起来不再工作了... "错误:无法将类型为'String'的值转换为预期的元素类型'AnyObject'",针对以下代码行: let b: [AnyObject] = ["bar"] - Jerem Lachkar

7
理解Swift中的打字和类型相关问题的关键在于所有路线都通向协议。
这个问题的挑战在于检测任何类型的数组,而不仅仅是一个具体的类型。 OP的示例失败了,因为[Any]不是基类或[字符串]的广义模式,也就是说,在Swift中,{{link1:[T]对T不是协变的}}。此外,您不能检查SequenceType或CollectionType,因为它们有关联类型(Generator.Element)。
因此,惯用的解决方案是使用一个标记协议来指示您想要匹配您的标准的类型。如下所示,通过创建一个空协议并将其与感兴趣的类型相关联来实现此目的。
import Foundation


protocol NestedType {}
extension Array: NestedType {}
extension Set: NestedType {}
extension Dictionary: NestedType {}
extension NSSet: NestedType {}

protocol AnyTypeOfArray {}
extension Array: AnyTypeOfArray {}
extension NSArray: AnyTypeOfArray {}

protocol AnyTypeOfDictionary {}
extension Dictionary: AnyTypeOfDictionary {}


func printType(v:Any) {
    if v is NestedType {
        print("Detected a nested type")
    }

    if v is AnyTypeOfArray {
        print("\t which is an array")
    }

    else if v is AnyTypeOfDictionary {
        print("\t which is a dictionary")
    }
}


printType([String:Int]())
printType([Int]())
printType(NSArray())

其输出结果为:

Detected a nested type
     which is a dictionary
Detected a nested type
     which is an array
Detected a nested type
     which is an array

这是正确的答案。而且最“Swifty”。 - Sentry.co

3
您可以通过将函数分成两个不同的实现(名称相同),一个接受Array,另一个接受其他所有内容来完成此操作。您还需要将它们变成通用函数,而不是使用Any类型。有了这样的设置,Swift可以使用类型推断来确定调用最佳函数的方法。
我会像这样实现它(我只是打印类型以显示结果所在的位置):
func parse<T>(parameter: T) {
    if parameter is Int {
        println("Int")
    } else if (parameter is Float) || (parameter is Double) {
        println("Double")
    } else if parameter is String {
        println("String")
    } else if parameter is Bool {
        println("Bool")
    } else {
        assert(false, "Unsupported type")
    }
}

func parse<T>(parameter: Array<T>) {
    println("Array")
    for element in parameter {
        // Recursively parsing...
        parse(element)
    }
}

然后像这样调用它:

parse(1)  // Int
parse(0.1) // Double
parse("asdf") // String
parse(true) // Bool
parse(["asdf", "asdf"]) // Array -> String String

输出:

Int
Double
String
Bool
Array
String
String

直接运行您的代码完美无缺。但是在我的代码环境中运行它会出现问题。我正在从类型为 [Any] 的数组中解包参数,并对每个元素调用 parse()。无论元素是什么,都只会调用第一个函数,而不是第二个函数。 - Ryan
这个失败了:parseAny([["a", "b", "c"]])func parseAny(values: [Any]) { for value in values { parse(value) } } - Ryan
同样的操作,只需要将parseAny([Any])替换为parseAny<T>(Array<T>)即可。 - Ryan
嗯,没错...我一时也不确定为什么,但我会想一想看能不能想出什么办法。 - Mike S
我能想到的唯一其他事情就是某种包装结构体。 - Ryan

0

您可以使用_stdlib_getTypeName函数,该函数返回给定值的名称。

例如:

    var myString = "String"
    var myInteger = 10
    var myArray = [10,22]
    var myDictionary = ["one": 1, "two": 2, "three": 3]
    println("\(_stdlib_getTypeName(myString))")
    println("\(_stdlib_getTypeName(myInteger))")
    println("\(_stdlib_getTypeName(myArray))")
    println("\(_stdlib_getTypeName(myDictionary))")

结果将会是:

_TtSS // for String
_TtSi // for integer
_TtSa // for array
_TtVSs10Dictionary // for dictionary

这个技术上可以让我知道何时给了我一个数组,但是如果不尝试强制转换,就不能将其视为数组。问题是,使用 parameter as [Any] 进行强制向下转换会导致运行时崩溃。此外,这种方法非常 hacky。 - Ryan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接