【问题标题】:Default equality operator for objects in SwiftSwift 中对象的默认相等运算符
【发布时间】:2014-11-12 01:52:13
【问题描述】:

当使用find在数组中搜索对象的索引时:

var index = find(Store.allItems, myItem)

编译器说Item 类不符合 Equatable 协议。

我的Item 类是一个对象,所以在我看来,来自 C#,默认的相等比较器应该是引用相等。

在 Swift 中,您似乎需要在每个引用类型上指定 Equatable 才能使 find 起作用。
此外,您需要实现 == 运算符,以便它使用 === 来比较引用:

func ==(a:Item, b:Item) -> Bool {
    return a === b
}

而且,这最后的声明必须是顶级代码,这让我很反感……

我试着写了:

func ==(a:AnyObject, b:AnyObject) -> Bool {
    return a === b
}

但它似乎不起作用。

是否有更快、更优雅的方法来实现对象的标准相等比较?

【问题讨论】:

  • 仅仅因为一个类符合 AnyObject,并不意味着它必须是 Equatable。这适用于难以单独使用 == 比较的类型,您可能希望使用自己的一组(更具体的)比较函数来进行比较。顶级代码只是意味着您在模块中看到Item 的任何地方,都可以进行比较。我个人认为这没有问题。
  • 除了find() 被定义为func find<C : CollectionType where C.Generator.Element : Equatable>(domain: C, value: C.Generator.Element) -> C.Index?。请注意,它要求元素符合Equatable。您确实意识到== 的任何定义都必须在全局范围内完成,因为它重载了全局运算符,这是一种常见的做法。同样,我不明白为什么这是一个问题。 Swift 不会自动知道您想如何比较 Item 类中的对象,并且由于 Swift 操作类型安全,如果这让您感到厌烦,也许可以考虑使用不同的语言。
  • 您将自己的经历与其他人的经历混为一谈。虽然我自己也很少重载运算符,但在这种情况下和这种语言显然是必要的。如果它确实解决了您的问题,为什么还要抱怨您必须做某事?这就像抱怨您必须将函数包装在花括号中。恐怕除非您是 Apple 工程师,否则您的意见不会影响 Swift 的工作方式,您可能只需要接受这个解决方案。
  • @Teejay:“Obj-C”不是“那样”。 NSObject 的实例就是这样。有区别。
  • @Teejay:“几乎所有语言都是如此。”您只是在谈论您的类子类化某些实现此功能的类的语言。在 Swift 中情况并非如此。

标签: swift


【解决方案1】:

试着让Item类符合Equatable:

extension Item:Equatable { }
func ==(lhs:Item, rhs:Item) -> Bool {
    return lhs === rhs
}

【讨论】:

  • 谢谢,但我直接在类上添加 Equatable 没有问题,而不是在扩展上,因为我自己编写了类。
  • 当然,class Item:Equatable { ... } 可以,随你喜欢。但我喜欢这种代码可见性模式。
  • 是的,但我更喜欢 Mike S 的回答,所以你有 == 覆盖所有类。
【解决方案2】:

如果您对实现自定义运算符不感兴趣,您可以实现另一个版本的 find() 来满足您的需求:

func find<C : CollectionType where C.Generator.Element : AnyObject>(domain: C, value: C.Generator.Element) -> C.Index? {
    var index = domain.startIndex
    while index != domain.endIndex {
        if domain[index] === value {
            return index
        }
        index = index.successor()
    }
    return nil
}

【讨论】:

  • 谢谢,但实际上我是尽量简化代码。
【解决方案3】:

您可以通过为 == 重载使用泛型函数来实现此功能;当然,您仍然必须让您的班级实现Equatable 协议。

实现看起来像这样:

func ==<T: AnyObject>(a: T, b: T) -> Bool {
    return a === b
}

class AClass: Equatable {}

测试一下:

let a = AClass()
let b = AClass()
let c = AClass()

let arr = [a, b, c]

let idxA = find(arr, a)
let idxB = find(arr, b)
let idxC = find(arr, c)

idxA、idxB 和 idxC 分别是 0、1 和 2。

【讨论】:

  • 谢谢,很好的解决方案。至少,顶层代码是通用代码。我会等待接受,看看其他人是否有不同的解决方案。
【解决方案4】:

如果你只想要一个引用相等,其实很简单。

只需让您的Item 类继承自NSObject。

class Item: NSObject {
        var name: String

        init(name: String) {
                self.name = name
        }
}

let item1 = Item(name: "item1")
let item2 = Item(name: "item2")
let item3 = Item(name: "item3")

let items = [item1, item2, item3]

for item in items {
        if let index = find(items, item) {
                println(items[index].name)
        }
}

// Console Output:
// item1
// item2
// item3

更新

对象比较

“当你比较两个时,有两种不同类型的比较 Swift 中的对象。第一个,相等(==),比较 对象。第二个,身份(===),确定是否 常量或变量引用同一个对象实例。

Swift 和 Objective-C 对象通常在 Swift 中使用 == 和 === 运算符。 Swift 提供了一个默认的实现 == 运算符用于派生自 NSObject 类的对象。在 这个操作符的实现,Swift 调用 isEqual: 方法 在 NSObject 类上定义。 NSObject 类只执行一个 身份比较,所以你应该实现自己的 isEqual: 方法 在派生自 NSObject 类的类中。因为你可以通过 Swift 对象(包括那些不是从 NSObject 派生的)到 Objective-C API,你应该为这些实现 isEqual: 方法 如果您希望 Objective-C API 比较 对象而不是它们的身份。

作为为您的班级实现平等的一部分,请务必实现 根据 Cocoa 中对象比较规则的哈希属性 核心竞争力。此外,如果你想使用你的类作为键 字典,也符合 Hashable 协议并实现 hashValue 属性。

摘自:Apple Inc.“将 Swift 与 Cocoa 和 Objective-C 结合使用”。 电子书。 https://itun.es/tw/1u3-0.l

【讨论】:

  • 继承 NSObject 有什么副作用吗?
  • 感谢您的回答。
  • @Teejay:“Obj-C”没有“那样工作”。 NSObject 就是这样工作的。有区别。
  • @Teejay:“因为 Obj-C 中的每个对象都继承自 NSObject”完全不正确。
  • @Teejay:毫无疑问,Objective-C 中并非所有对象都继承自NSObject。请参阅 NSProxy 以获取 Cocoa 中的示例。您可以创建自己的不继承自NSObject 的类。你说,NSObject 是大多数对象的根。这是真的,与我所说的没有冲突,我没有看到任何要回复的内容。
猜你喜欢
  • 2021-05-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多