对于 Friday Fun,我想以可互换的格式对 Angles 进行建模。我不确定我是否以最 Swift 惯用的方式完成了它,但我正在学习。所以我有一个 Angle
协议(protocol),然后是 3 种不同的结构类型(弧度、度数和旋转),它们都符合 Angle 协议(protocol)。我希望能够添加/减去它们,但诀窍是,我希望 lhs
参数指示返回类型。例如:
Degrees(180) + Rotations(0.25) --> Degrees(270)
或
Rotations(0.25) + Radians(M_PI) -> Rotations(0.75)
我希望我能做类似的事情
func + (lhs:Angle, rhs:Angle) -> Angle {
return lhs.dynamicType(rawRadians: lhs.rawRadians + rhs.rawRadians)
}
Angle
协议(protocol)需要一个 var rawRadians:CGFloat { get }
以及一个 init(rawRadians:CGFloat)
我可以使用类似 Smalltalk 的双重分派(dispatch)方法来做到这一点,但我认为大多数情况下都有更适合 Swift 的方法(尤其是需要较少代码的方法,双重分派(dispatch)需要大量样板代码)。
最佳答案
你只需要一个通用的添加:
func +<A: Angle>(lhs: A, rhs: Angle) -> A {
return A(rawRadians: lhs.rawRadians + rhs.rawRadians)
}
这样加法将返回 lhs 上的任何类型。
一般来说,如果您使用 dynamicType
,你可能正在与 Swift 战斗。 Swift 更多地依赖于泛型和协议(protocol)(即编译时的静态类型信息)而不是动态调度(即运行时的动态类型信息)。
在这段代码中,如您所说,A
是“某种类型,符合 Angle
,在编译时确定”的占位符。所以在你的第一个例子中:
Degrees(180) + Rotations(0.25) --> Degrees(270)
这实际上调用了一个专门的函数 +<Degrees>
.还有这个:
Rotations(0.25) + Radians(M_PI) -> Rotations(0.75)
调用一个(逻辑上)不同的函数 +<Rotations>
.编译器可能会选择将这些函数优化为单个函数,但逻辑上它们是独立的函数,在编译时创建。它基本上是编写 addDegrees(Degrees, Angle)
的快捷方式和 addRotations(Rotations, Angle)
手工制作。
现在,关于一个接受两个角度并返回的函数的问题……好吧,什么?如果你想返回 Angle
在这种情况下,这很简单,并且与您的原始签名完全匹配:
func +(lhs: Angle, rhs: Angle) -> Angle {
return Radians(rawRadians: lhs.rawRadians + rhs.rawRadians)
}
“但是......”你是说,“返回 Radians
。”不,它没有。它返回 Angle
.你可以在上面做任何你想做的“类似角度”的事情。实现细节本应是不透明的。如果您关心底层数据结构是 Radians
,您几乎可以肯定做错了什么。
好吧,有一种情况,了解这一点可能会有用,那就是如果你根据你的方式打印出来。因此,如果用户为您提供学位信息作为开始,那么您希望以度为单位打印所有内容(使用您未提及的 description
方法)。也许在那种特殊情况下值得这样做。如果你愿意,你的原始代码非常接近:
func +(lhs: Angle, rhs: Angle) -> Angle {
return lhs.dynamicType.init(rawRadians: lhs.rawRadians + rhs.rawRadians)
}
但重要的是要了解这与您要求“使用 lhs 参数来指示返回类型”的要求不符。这导致 lhs 参数指示返回实现。返回的类型总是Angle
.如果要更改返回类型,则需要使用泛型。
关于用于返回接收者类型(符合协议(protocol))的协议(protocol)的 Swift 二元运算符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32788148/