如何检查一个对象是否为集合?(Swift)

7

我广泛使用KVC来构建应用程序需要的统一接口。例如,我的一个函数会获取一个对象,该对象仅基于字符串键的字典进行多次检查。

因此,我需要一种方法来检查按键排序的对象是否为集合类型。

我原本期望能够进行一些协议检查(例如在C#中的IEnumerable检查是否可枚举),但这并没有成功:

if let refCollection = kvcEntity.value(forKey: refListLocalKey) as? AnySequence<CKEntity> { ... }

我也尝试了AnyCollection。

我知道只需输入以下命令就可以遍历所有主要集合类型:

if let a = b as? Set { ...} // (or: if a is Set {...})
if let a = b as? Array { ...}
if let a = b as? Dictionary { ...}

但从继承/多态的角度来看,这似乎不太合适。

可能是使用Swift的isKindOfClass的重复问题。 - Anni S
你尝试过 AnyCollection,但没有尝试 Collection 吗?Collection 是应该尝试的协议。它是直接被最常见的类型如 Dictionary、Set 等采用的协议。我认为 AnyCollection 并不包含新的原生 Swift 类型。 - Michael Fourre
@Hexfire,如果你想检查它是否符合协议,可以参考https://dev59.com/p14c5IYBdhLWcg3wQoY5。希望这能帮到你。 - Anni S
2
@AnniS 这在Collection中不起作用,会导致错误。 - rmaddy
2
@AnniS,conformsToProtocol来自objc,仅适用于NSObject派生类型。Swift集合不适用。 - Hexfire
显示剩余5条评论
3个回答

5

Collection不能再用于类型检查,因此Ahmad F的解决方案将不再编译。

我进行了一些调查。有些人建议桥接到obj-c集合并使用isKindOfClass,而其他人则尝试使用反射(通过使用Mirror)。两者都不令人满意。

如果我们关注的是ArrayDictionarySet(列表可以更新),那么有一种相当直接、有点粗糙但有效的方法来完成任务:

func isCollection<T>(_ object: T) -> Bool {
    let collectionsTypes = ["Set", "Array", "Dictionary"]
    let typeString = String(describing: type(of: object))

    for type in collectionsTypes {
        if typeString.contains(type) { return true }
    }
    return false
}

使用方法:

var set : Set! = Set<String>()
var dictionary : [String:String]! = ["key" : "value"]
var array = ["a", "b"]
var int = 3
isCollection(int) // false
isCollection(set) // true
isCollection(array) // true
isCollection(dictionary) // true

硬编码是一种缺点,但它可以完成工作。


1
这会对像 struct MyNonSet { } 这样的东西产生误报。 - Cristik
此解决方案不支持符合“Collection”自定义类型。 - lukas

2

注意:此解决方案不适用于Swift 5+。

func isCollection<T>(object: T) -> Bool {
    switch object {
    case _ as Collection:
        return true
    default:
        return false
    }
}

调用:

// COLLECTION TESTING //

let arrayOfInts = [1, 2, 3, 4, 5]
isCollection(object: arrayOfInts) // true

let setOfStrings:Set<String> = ["a", "b", "c"]
isCollection(object: setOfStrings) // true

// [String : String]
let dictionaryOfStrings = ["1": "one", "2": "two", "3": "three"]
isCollection(object: dictionaryOfStrings) // true


// NON-COLLECTION TESTING //

let int = 101
isCollection(object: int) // false

let string = "string" // false

let date = Date()
isCollection(object: date) // false

1
在 Swift 5 中:错误 "协议 'Collection' 只能用作泛型约束,因为它具有 Self 或关联类型要求"。 - Luc-Olivier

1
你可以创建另一个协议。
protocol CountableCollection {
    var count: Int { get }
}

extension Array: CountableCollection where Element: Any {
}

extension Dictionary: CountableCollection where Key == String, Value == Any {
}

将您需要的所有方法从Collection添加到新创建的协议中(我只添加了count getter以进行演示)。
在此之后,您可以简单地执行以下操作:
if someVar is CountableCollection {
    print(someVar.count)
}

如果someVar是一个Array或者Dictionary,它将会是真(true)。如果需要,你也可以让它符合Set


这对于在第三方库中定义的自定义集合甚至在自己的代码中定义的集合(例如 struct MyAwesomeCollection: Collection)都不起作用。 - Cristik

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