我希望编写允许我在不同类型之间建立绑定的代码,如下所示:

Map.add(1.0).to(CGPointZero) // (x:1.0, y:1.0)

这是一个人为的示例,但是如果它可以工作,则可能是捕获关系的一种好方法。在C++中,这很简单,您可以创建如下代码:
class Binding<typename FromType> {

    FromType from;

    to<typename ToType>(ToType toType) {
        return from + toType
    }
}

class Map {

     Binding<T> add<T>(t:T) {
        return Binding<T>(t:t)
    }
}

然后编译器会确定是否可以添加这些类型。

Swift是另一种动物,您需要先通过协议捕获这种关系。它可能看起来像这样:
public protocol ScalarArithmetic {
    typealias SomeScalarType

     func +(lhs: SomeScalarType, rhs: Self) -> Self
}

//  Double + CGPoint
public func + (lhs:Double, rhs:CGPoint) -> CGPoint {
    return CGPoint(x: rhs.x + CGFloat(lhs), y:rhs.y + CGFloat(lhs))
}

//  Float + CGPoint
public func + (lhs:Float, rhs:CGPoint) -> CGPoint {
    return CGPoint(x: rhs.x + CGFloat(lhs), y:rhs.y + CGFloat(lhs))
}


class Binding<T> {
    var from: T

    init(t:T) { self.from = t }

    func to<S:ScalarArithmetic where S.SomeScalarType == T>(s:S) -> S {
        return self.from + s
    }
}

class Map {

    class func add<T>(t:T) -> Binding<T> {
        return Binding<T>(t:t)
    }
}

但是,问题是如何绑定类型-我们以CGPoint为例,它具有多个标量类型,因此上述机制可用于一系列绑定(例如上述C++示例)。

这不起作用,因为编译器不喜欢重复的语句:
extension CGPoint : ScalarArithmetic {
    typealias SomeScalarType = Double

}

extension CGPoint : ScalarArithmetic {
    typealias SomeScalarType = Float
}

我尝试按照其自己的协议捕获标量类型-也许这就是这样做的方法,但是这将问题推了很深:
public protocol SomeScalarType {}
extension Double: SomeScalarType {}
extension Float: SomeScalarType {}

public protocol ScalarArithmetic {
    func +(lhs: SomeScalarType, rhs: Self) -> Self
}

//  Double + CGPoint
public func + (lhs:Double, rhs:CGPoint) -> CGPoint {
    return CGPoint(x: rhs.x + CGFloat(lhs), y:rhs.y + CGFloat(lhs))
}

//  Float + CGPoint
public func + (lhs:Float, rhs:CGPoint) -> CGPoint {
    return CGPoint(x: rhs.x + CGFloat(lhs), y:rhs.y + CGFloat(lhs))
}

extension CGPoint: ScalarArithmetic {} // does not conform to ScalarArithmetic
// since it is not implemented in terms of the ScalarArithmetic protocol,
// despite having the necessary operators.

因此,让我自己回答我的问题,这是否留为唯一选择,使ScalarType“可转换足够”,从而可以将其转换为所需的原始类型:
public func + (lhs:SomeScalarType, rhs:SomeScalarType) -> CGPoint {
    return CGPoint(x: rhs.x + CGFloat(lhs), y:rhs.y + CGFloat(lhs))
}

我将如何做和/或有更好的解决方案?

提前致谢。

最佳答案

Swift不会为您强制转换类型。这是一种语言选择,而要绕开它将会很困难。您不能+任意数字类型。您的C++示例之所以有效,是因为可以添加任意数字,而在Swift中则不能。

IMO,您要执行的操作的正确答案是:

public func + (lhs:Double, rhs:CGPoint) -> CGPoint {
    return CGPoint(x: rhs.x + CGFloat(lhs), y:rhs.y + CGFloat(lhs))
}

句号但这是附加到+的可怕事情。您不能对scalar和向量进行+有意义的编码。这种事情最好定义为:
public func + (lhs:CGPoint, rhs:CGPoint) -> CGPoint {
    return CGPoint(x: rhs.x + lhs.x, y:rhs.y + lhs.y)
}

然后制作要添加的CGPoint(即使您必须创建一个Diagonal(1)函数来提供帮助。我知道这是一个人为的示例,但我希望对于整个问题也是如此。使类型自动变强应使用极其小心:创建一个init几乎总是更好,它可以让您显式地将一个转换为另一个,然后运算符才有意义。

这是Swift的全部优势,可提供更强大的类型安全性。例如,我有一些这样的代码:
// Frequency and Time are reciprocols
public func *(lhs: SignalTime,      rhs: SignalFrequency) -> Double { return lhs.seconds * rhs.hertz }
public func *(lhs: SignalFrequency, rhs: SignalTime)      -> Double { return rhs * lhs }

public func /(lhs: Double, rhs: SignalFrequency) -> SignalTime { return SignalTime(seconds: lhs / rhs.hertz) }
public func /(lhs: Double, rhs: SignalTime)      -> SignalFrequency { return SignalFrequency(hertz: lhs / rhs.seconds) }

// Frequency can be scaled by a constant
public func *(lhs: SignalFrequency, rhs: Double)          -> SignalFrequency { return SignalFrequency(hertz: lhs.hertz * rhs) }
public func *(lhs: Double,          rhs: SignalFrequency) -> SignalFrequency { return rhs * lhs }

这都是非常明确的,甚至是乏味的,但应该如此。有组合这些类型的有用方法,而有组合这些类型的非法方法。 x/y返回的类型与y/x完全不同。这样做在编译阶段遇到了几个“哦,我不小心将一个频率乘以一个频率”的错误。试图用最少的代码使所有这些“神奇地起作用”往往会以允许非法操作的方式过度扩展类型。

在您有一个非常好的且明确的用例之前,我将避免使用ScalarArithmetic。如果您有其他示例,那么看一下会很有趣,但是我希望答案是相似的。小心不要过度强迫。

关于ios - Swift:协议(protocol):捕获类型之间的兼容性,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/25874300/

10-14 21:40
查看更多