首页 > 解决方案 > 为数组扩展选择类型

问题描述

我正在尝试扩展Array.contains函数以允许可选参数并在参数为 nil 时返回 false。我从这个开始:

extension Array {
    func contains(_ element: Element?) -> Bool {
        if let element = element {
            return self.contains(element)
        } else {
            return false
        }
    }
}

当参数为 nil 时,调用代码会正确找到我的函数版本。然而,self.contains这个函数内部并没有调用原始版本——它调用自己并创建一个无限循环。有没有办法让self.contains线路调用原始函数?

接下来,我尝试用self.contains不同的实现替换,但我想不出任何不需要将扩展​​限制为 的东西Element: Equatable,如下所示:

extension Array where Element: Equatable {
    func contains(_ element: Element?) -> Bool {
        if let element = element {
            return (self.firstIindex(of: element) != nil)
        } else {
            return false
        }
    }
}

但是,这使得该功能无法用于 UIButton、Dictionary 和其他我需要使用它的元素类型。原始contains函数如何为这些类型执行此操作?(我搜索并找不到它的源代码。)

接下来,我删除了约束并更改了方法签名以消除我的扩展函数和原始函数之间的歧义:

extension Array {
    func contains(optional element: Element?) -> Bool {
        if let element = optional {
            return self.contains(element)
        } else {
            return false
        }
    }
}

但是当我更改方法签名时,编译器错误告诉我该self.contains行现在要求 Element 符合 Equatable。原始函数如何免除此限制,而签名略有不同的函数需要它?

我觉得这应该是微不足道的,但我已经花了几个小时,找不到工作设置。有人可以告诉我一个解决方案吗?

标签: swifttypescollectionsprotocolsextension-methods

解决方案


在看到@Sweeper 关于 UIButton 和 Dictionary 是平等的评论后,我再次尝试使用该约束。我之前在 UIButtons 数组中看到过错误,但这次没有,所以这些肯定是由其他东西触发的。然而,我认为是字典的错误实际上只是简单的结构。我使用这个答案使我的结构符合 Equatable,现在一切都按预期工作。

事实上,添加了 Equatable 约束后,不再出现无限循环,所以我可以回到我的函数的第一个版本:

extension Array where Element: Equatable {
    func contains(_ element: Element?) -> Bool {
        if let element = element {
            return self.contains(element)
        } else {
            return false
        }
    }
}

显然有一个版本contains()没有 Equatable 约束,这是我之前使用结构时调用的,以及我的自定义函数使用self.contains(). 无论如何,这些是吸取的教训:

  1. 创建原生函数的变体时,添加与原始函数相同的类型约束。

  2. 使自定义结构符合 Equatable 以便与集合一起使用。


推荐阅读