2015-09-25 49 views
0

对于星期五乐趣,我想以可互换的格式为角度建模。我不确定我是否以最快捷的惯用方式完成了它,但我正在学习。所以我有一个Angle协议,然后是3种不同的结构类型(Radians,Degrees和Rotations),它们都符合Angle协议。我想能够加/减他们,但诀窍是,我想要lhs参数来指定返回类型。例如:Swift二元运算符用于返回接收器类型(符合协议)的协议

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-es做到这一点que双重调度方法,但我认为最适合使用更多Swift的方法(特别是需要更少代码的代码,双重调度需要大量样板代码)。

回答

2

你只需要一个普通的加法:

func +<A: Angle>(lhs: A, rhs: Angle) -> A { 
    return A(rawRadians: lhs.rawRadians + rhs.rawRadians) 
} 

通过这种方式,除了将返回任何类型的LHS。

一般来说,如果你使用dynamicType,你可能与Swift战斗。 Swift更依赖泛型和协议(即编译时的静态类型信息),而不是动态分派(即运行时的动态类型信息)。

在此代码中,正如您所说,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,这很容易,正好和你原来的签名一致:“返回Radians

func +(lhs: Angle, rhs: Angle) -> Angle { 
    return Radians(rawRadians: lhs.rawRadians + rhs.rawRadians) 
} 

“但是......”你说,不,它不。它返回Angle。你可以做任何“角度”的事情就可以了。实现细节应该是不透明的。如果你关心底层的数据结构是Radians,那么你几乎肯定会做出错误的事情。

好的,有一个方面可能有助于了解这一点,也就是说,如果您根据自己的方式打印出来的内容。所以如果用户给你开始度数信息,那么你想打印所有度数(使用你没有提到的description方法)。也许这是值得做的,你希望将特定case.If,你原来的代码是非常接近:

func +(lhs: Angle, rhs: Angle) -> Angle { 
    return lhs.dynamicType.init(rawRadians: lhs.rawRadians + rhs.rawRadians) 
} 

但它的关键是要理解,这不符合您的要求,有” LHS参数决定返回类型“。这导致lhs参数指定返回实现。返回类型始终为Angle。如果您想更改类型的返回,则需要使用泛型。