我如何制作一个通用协议,使我能够在任何有意义的结构或类中实现`add`方法?

时间:2015-10-05 17:42:18

标签: swift

我希望能够做到这样的事情。

let float1: Float = 1.0
let float2: Float = 2.0
let float3 = f1.add(f2) // Float of value 3.0

let cgFloat1: CGFloat = 1.0
let cgFloat2: CGFloat = 2.0
let cgFloat3 = cgFloat1.add(cgFloat2) // CGFloat of value 3.0

let number1 = NSNumber(float: 1.0)
let number2 = NSNumber(float: 2.0)
let number3 = number1.add(number2)  // NSNumber of value 3.0

如何创建一个允许我在任意结构或类上实现add的协议?

我发现使用Swift非常困难。

1 个答案:

答案 0 :(得分:4)

第一步是描述协议中的内容:

 protocol Addable {
    func add(other : Self) -> Self
 }

然后你可以让其他类型符合它:

extension Int : Addable {
   func add(other : Int) -> Int {
       return self + other
   }
}

当然这意味着必须为要添加此add方法的每种类型编写相同的代码。我们可以通过使用协议扩展来提供默认实现,从而做得更好。但我们不能只为Addable实现扩展,编译器会抱怨"二进制运算符+不能应用于两个自操作符"。这是因为该协议可以通过没有定义+运算符的类型来实现。所以我们需要使用' where'子句将扩展名限制为定义+运算符的类型。这意味着我们需要另一个协议,它需要+运算符。可悲的是,斯威夫特并没有提供一个,所以我们需要自己制作:

protocol DefinesPlusOperator {
   func +( lhs : Self, rhs : Self ) -> Self
}

现在我们可以将此协议添加到我们所有的类型中:

extension Int : DefinesPlusOperator {}
extension Float : DefinesPlusOperator {}
extension CGFloat : DefinesPlusOperator {}

请注意,我们不需要在此处提供实现,这些类型已经具有+运算符,因此我们可以声明它们符合此协议,而无需提供实现。

有了这个,我们可以为符合Addable和DefinesPlusOperator协议的所有类型提供默认实现:

extension Addable where Self : DefinesPlusOperator {
   func add(other : Self) -> Self {
      return self + other
   }
}

同样,我们必须扩展我们的类型以符合这个协议:

extension Int : Addable {}
....

这里我们不必提供实现,因为我们在协议扩展中有默认实现。

我们也可以声明类型同时符合这两个协议:

extension Double : DefinesPlusOperator, Addable {}

离开NSNumber。这是最难的情况。它没有提供+运算符,因此我们不能将它与DefinesPlusOperator默认实现一起使用。我们必须创建自己的实现:

extension NSNumber : Addable {
   func add( other : NSNumber ) -> Self {
      return self.dynamicType.init(integer: integerValue + other.integerValue)
   }
}

这看起来有点奇怪,但它必须与此类似,因为NSNumber不是最终类,因此它可以是子类,并且协议必须适用于所有子类以及基类。使用self.dynamicType.init创建结果实例可确保我们返回与调用add时完全相同的NSNumber子类。

不幸的是,这会在运行时崩溃,因为NSNumber的实现方式是类集群。 init(integer :)初始化程序在所有具体的NSNumber子类上都不可用。

我们仍然可以为NSNumber添加一个add方法,但我们无法使其符合该Addable协议。但这也不是那么容易,因为NSNumber可以包含不同类型的值(布尔值,整数,浮点数,NSDecimalNumber)。因此,如果我们像这样实施它,我们就会丢失信息:

 extension NSNumber {
     func add(other : NSNumber) -> NSNumber {
        return NSNumber(integer: integerValue + other.integerValue)
     }
 }

我对NSNumber的处理建议是在收到它时立即将它转换为正确的Swift类型,如果我们必须将其转换为Objective-C API,则只将其转换回来。