为什么必须将协议运算符实现为全局函数?

时间:2016-02-06 20:19:01

标签: ios swift equatable

我已经看到了这个Swift Equatable Protocol问题的答案,提到了==方法必须在全球范围内宣布的方式。

如果我不采用Equatable,我仍然可以声明==来测试我的两种类型之间的相等性。

// extension Foo: Equatable {}

func ==(lhs: Foo, rhs: Foo) -> Bool {
    return lhs.bar == rhs.bar
}

struct Foo {
    let bar:Int
}

即使EquatableEquatable通过。

Intent myIntentForWhatsapp = new Intent(Intent.ACTION_SEND); myIntentForWhatsapp.setType("text/plain"); myIntentForWhatsapp.setPackage("com.whatsapp"); myIntentForWhatsapp.putExtra(Intent.EXTRA_TEXT, "Hallo, this is a msg"); try { activity.startActivity(myIntentForWhatsapp); } catch (android.content.ActivityNotFoundException ex) { System.out.println("Whatsapp not installed."); } 协议如何比仅仅让(我们和)编译器安全地知道我们的类型实现了协议所需的方法的语法糖更多?

为什么必须全局声明运算符实现,即使对于协议也是如此?这是由于调度操作员的某种不同方式吗?

5 个答案:

答案 0 :(得分:22)

更新

从Xcode 8 beta 4发行说明:

  

可以在类型或扩展中定义运算符。例如:

struct Foo: Equatable {
    let value: Int
    static func ==(lhs: Foo, rhs: Foo) -> Bool {
        return lhs.value == rhs.value
    }
}
     

此类运算符必须声明为static(或在类class final内),并且具有相同的运算符   签名作为他们的全球同行。作为此更改的一部分,运营商要求已声明   协议也必须明确声明static

protocol Equatable {
    static func ==(lhs: Self, rhs: Self) -> Bool
}

ORIGINAL

This was discussed on the swift-evolution list recently (2016-01-31 through 2016-02-09 so far).以下是Chris Lattner所说的关于在结构或类范围内声明运算符的内容:

  

是的,这是一个通常需要的功能(至少对称运算符)。在类声明中动态调度运算符也很有用。我不认为我们有一个坚定的建议,即确定名称查找的工作原理。

后来(回复Haravikk):

  
    

名称查找问题是什么?你的意思是Foo == Foo的运算符存在于多个位置的情况吗?

  
     

是。名称查找必须具有明确定义的搜索顺序   定义阴影和无效的多重定义规则。

     
    

就个人而言,我只是坚持我们现在拥有的东西,即将特定类/结构中的运算符实现视为全局     无论如何定义并在声明相同签名时抛出错误     不止一次。

  
     

我们需要多个模块才能定义一个实例   运营商,我们需要运营商扩展,我们需要追溯   与任何其他成员一样,符合工作要求。

答案 1 :(得分:7)

来自documentation

的说明
  

操作符函数被定义为具有函数的全局函数   与要重载的运算符匹配的名称。

     

该函数是全局定义的,而不是作为一个方法   目标类或结构,以便它可以用作中缀运算符   在目标类或结构的现有实例之间。

我用通用表达式目标类或结构替换引号中具体结构(Vector2D)的名称

答案 2 :(得分:7)

  

它的实现需要在全局范围内声明,这使得它看起来是偶然的和不同于协议,即使采用Equatable也是如此。

每个协议都是如此,无论是需要全局函数,类方法还是实例方法。您总是可以独立于是否存在需要它的协议来实现。

  

Equatable协议如何比语法糖更能让我们(我们和)编译器安全地知道我们的类型实现了协议所需的方法?

那不是糖。这是协议的定义和协议的整个要点。它告诉你这种类型可以应用这些东西。

  

为什么必须全局声明运算符实现,即使对于协议也是如此?这是由于调度操作员的某种不同方式吗?

因为那是Swift的语法。运算符函数实现必须是全局函数。 Swift团队一直有兴趣改变它以使其更加一致,但今天这就是Swift的工作方式。

答案 3 :(得分:1)

之前有点麻烦,因为您需要手动执行Equatable的实现。尽管Rob Napier提出了一个好的观点,那就是协议的目的。

但是不再了,也就是说,通过一致性,您将获得 Automated Synthesis ,并且不再需要样板代码。

struct Country: Equatable {
  let name: String
  let capital: String
  var visited: Bool
}

就是这样! 编译器将完成其余工作。

let france = Country(name: "France", capital: "Paris", visited: true)
let spain = Country(name: "Spain", capital: "Madrid", visited: true)
if france == spain { ... } // false

话虽这么说,但仍然可以覆盖==函数的实现。

有关更多信息,请参见here

答案 4 :(得分:0)

在Swift标准库中,定义了'=='运算符。它没有比较的一侧或另一侧的关联性,并且在整个Swift运算符顺序中具有编号优先级。如果你在'import Swift'上点击Swift,你可以检查这个

infix operator == {
    associativity none
    precedence 130
}

对于Int,该库已经将Int / Int8 / Int16 / Int32 / Int64作为Equatable:

public func ==(lhs: Int, rhs: Int) -> Bool

和许多其他类型一样。

Int在扩展中成为Hashable(在哪里符合该协议,你需要创建一个var hashValue:Int {get},它会随着app的每次执行而改变:

extension Int : Hashable {
    public var hashValue: Int { get }
}

运算符'=='和协议通常需要在顶层的struct / enum / class之外声明,因为你正在扩展语言,现在,这就是编译的快捷方式;所有Swift的运算符都是在全局范围内定义的属性。

对于你的'Foo',你通过添加func:

采用了Equatable
func ==(lhs: Foo, rhs: Foo) -> Bool {
    return lhs.bar == rhs.bar
}

使用协议中的“Self”局部变量:

public protocol Equatable {
    public func ==(lhs: Self, rhs: Self) -> Bool
}