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