Swift 2.0:参数化的 class 如果它继承自 class 是可等式的,则不会调用适当的 == 函数
Swift 2.0: Parametrized classes don't call proper == function if it inherits from class that is Equatable
当参数化 class 继承自另一个符合 Equatable
的 class 时,==
调用超级 class 的 ==
。谁能解释为什么会这样 and/or 我怎么可能在这里做错了什么?我相信一个例子最能说明我的问题:
public class Foo: Equatable {}
public func ==(lhs: Foo, rhs: Foo) -> Bool { return false }
//Parametrized
public class Bar<T: Equatable>: Foo {
public var bar: T?
public init(barIn: T?) {
self.bar = barIn
}
}
public func ==<T>(lhs: Bar<T>, rhs: Bar<T>) -> Bool { return lhs.bar == rhs.bar }
//Non parametrized
public class Baz: Foo {
public var baz: Int?
public init(bazIn: Int?) {
self.baz = bazIn
}
}
public func ==(lhs: Baz, rhs: Baz) -> Bool { return lhs.baz == rhs.baz }
//Parametrized, no inheritance
public class Qux<T: Equatable>: Equatable {
public var qux: T?
public init(quxIn: T?) {
self.qux = quxIn
}
}
public func ==<T>(lhs: Qux<T>, rhs: Qux<T>) -> Bool { return lhs.qux == rhs.qux }
Bar<Int>(barIn: 1) == Bar<Int>(barIn: 1) //false
Baz(bazIn: 1) == Baz(bazIn: 1) //true
Qux(quxIn: 1) == Qux(quxIn: 1) //true, of course
不能说我是 Swift 运算符重载方面的专家,但我发现 an article 你可以找到一些有用的信息:
For reference types, the equality becomes conflated with identity. It
makes sense that two Name structs with the same values would be equal,
but two Person objects can have the same name, but be different
people.
For Objective-C-compatible object types, the == operator is already
provided from the isEqual
: method:
....
For Swift reference types, equality can be evaluated as an identity
check on an ObjectIdentifier
constructed with an instance of that
type:
请也考虑这个 :
The reason the equality for A is being invoked for an Array that
contains B is that overloading of free functions is resolved
statically, not dynamically – that is, at compile time based on the
type, not at runtime based on the pointed-to value.
This is not surprising given == is not declared inside the class and
then overridden in the subclass. This might seem very limiting but
honestly, defining polymorphic equality using traditional OO
techniques is extremely (and deceptively) difficult. See this link and
this paper for more info.
The naïve solution might be to define a dynamically dispatched
function in A, then define ==
to just call that: ... Then when you
implement B, you’d override equalTo
:
因此,您的代码无法正常工作的原因可能是编译器在 静态 解析后调用它,而这是在不知道您会 override ==
运算符,用于 Foo
.
的某些锚点
可能你应该这样做(相等逻辑移动到从运算符调用的函数:
public class Foo: Equatable {
func equalTo(rhs: Foo) -> Bool {
// base logic here
}
}
public func ==(lhs: Foo, rhs: Foo) -> Bool {
// check for type here and call appropriate function]
// may be this will be done automatically as Bar overloads equalTo function
return lhs.equalTo(rhs)
}
public class Bar<T: Equatable>: Foo {
public var bar: T?
public init(barIn: T?) {
self.bar = barIn
}
override func equalTo(rhs: Foo) {
// cast rhs to Foo here
// if it can't be done, return false
return (rhs as? Foo).map { foo in
return self.bar == foo.bar
} ?? false
}
}
虽然我在 Swift 参考资料中没有找到任何关于此的内容,this gives us a clue:
Generics are lower down the pecking order. Remember, Swift likes to be as “specific” as possible, and generics are less specific. Functions with non-generic arguments (even ones that are protocols) are always preferred over generic ones:
虽然这似乎与 Equatable
没有任何关系;该测试向我们展示了相同的行为:
class Foo {};
class Bar<T>: Foo {};
class Baz: Bar<Int> {};
class Qux<T>: Baz {};
func test(foo: Foo) {
print("Foo version!");
};
func test<T>(bar: Bar<T>) {
print("Bar version!");
};
func test(baz: Baz) {
print("Baz version!");
};
func test<T>(qux: Qux<T>) {
print("Qux version!");
};
let foo = Foo();
let bar = Bar<Int>();
let baz = Baz();
let baz2: Bar<Int> = Baz();
let qux = Qux<Float>();
test(foo); // Foo
test(bar); // Foo
test(baz); // Baz
test(baz2); // Foo
test(qux); // Baz
所以这里发生的事情是 在选择一个自由函数时,以及使用它的静态类型而不是动态类型时,Swift 不喜欢使用任何泛型],即使那个泛型是类型参数而且确实应该是最专业的选择。
所以,似乎要解决这个问题,正如@VMAtm 所建议的那样,您应该将 equalTo
之类的方法添加到 class 中,以便实际方法在运行时间。
当参数化 class 继承自另一个符合 Equatable
的 class 时,==
调用超级 class 的 ==
。谁能解释为什么会这样 and/or 我怎么可能在这里做错了什么?我相信一个例子最能说明我的问题:
public class Foo: Equatable {}
public func ==(lhs: Foo, rhs: Foo) -> Bool { return false }
//Parametrized
public class Bar<T: Equatable>: Foo {
public var bar: T?
public init(barIn: T?) {
self.bar = barIn
}
}
public func ==<T>(lhs: Bar<T>, rhs: Bar<T>) -> Bool { return lhs.bar == rhs.bar }
//Non parametrized
public class Baz: Foo {
public var baz: Int?
public init(bazIn: Int?) {
self.baz = bazIn
}
}
public func ==(lhs: Baz, rhs: Baz) -> Bool { return lhs.baz == rhs.baz }
//Parametrized, no inheritance
public class Qux<T: Equatable>: Equatable {
public var qux: T?
public init(quxIn: T?) {
self.qux = quxIn
}
}
public func ==<T>(lhs: Qux<T>, rhs: Qux<T>) -> Bool { return lhs.qux == rhs.qux }
Bar<Int>(barIn: 1) == Bar<Int>(barIn: 1) //false
Baz(bazIn: 1) == Baz(bazIn: 1) //true
Qux(quxIn: 1) == Qux(quxIn: 1) //true, of course
不能说我是 Swift 运算符重载方面的专家,但我发现 an article 你可以找到一些有用的信息:
For reference types, the equality becomes conflated with identity. It makes sense that two Name structs with the same values would be equal, but two Person objects can have the same name, but be different people.
For Objective-C-compatible object types, the == operator is already provided from the
isEqual
: method: .... For Swift reference types, equality can be evaluated as an identity check on anObjectIdentifier
constructed with an instance of that type:
请也考虑这个
The reason the equality for A is being invoked for an Array that contains B is that overloading of free functions is resolved statically, not dynamically – that is, at compile time based on the type, not at runtime based on the pointed-to value.
This is not surprising given == is not declared inside the class and then overridden in the subclass. This might seem very limiting but honestly, defining polymorphic equality using traditional OO techniques is extremely (and deceptively) difficult. See this link and this paper for more info.
The naïve solution might be to define a dynamically dispatched function in A, then define
==
to just call that: ... Then when you implement B, you’d overrideequalTo
:
因此,您的代码无法正常工作的原因可能是编译器在 静态 解析后调用它,而这是在不知道您会 override ==
运算符,用于 Foo
.
的某些锚点
可能你应该这样做(相等逻辑移动到从运算符调用的函数:
public class Foo: Equatable {
func equalTo(rhs: Foo) -> Bool {
// base logic here
}
}
public func ==(lhs: Foo, rhs: Foo) -> Bool {
// check for type here and call appropriate function]
// may be this will be done automatically as Bar overloads equalTo function
return lhs.equalTo(rhs)
}
public class Bar<T: Equatable>: Foo {
public var bar: T?
public init(barIn: T?) {
self.bar = barIn
}
override func equalTo(rhs: Foo) {
// cast rhs to Foo here
// if it can't be done, return false
return (rhs as? Foo).map { foo in
return self.bar == foo.bar
} ?? false
}
}
虽然我在 Swift 参考资料中没有找到任何关于此的内容,this gives us a clue:
Generics are lower down the pecking order. Remember, Swift likes to be as “specific” as possible, and generics are less specific. Functions with non-generic arguments (even ones that are protocols) are always preferred over generic ones:
虽然这似乎与 Equatable
没有任何关系;该测试向我们展示了相同的行为:
class Foo {};
class Bar<T>: Foo {};
class Baz: Bar<Int> {};
class Qux<T>: Baz {};
func test(foo: Foo) {
print("Foo version!");
};
func test<T>(bar: Bar<T>) {
print("Bar version!");
};
func test(baz: Baz) {
print("Baz version!");
};
func test<T>(qux: Qux<T>) {
print("Qux version!");
};
let foo = Foo();
let bar = Bar<Int>();
let baz = Baz();
let baz2: Bar<Int> = Baz();
let qux = Qux<Float>();
test(foo); // Foo
test(bar); // Foo
test(baz); // Baz
test(baz2); // Foo
test(qux); // Baz
所以这里发生的事情是 在选择一个自由函数时,以及使用它的静态类型而不是动态类型时,Swift 不喜欢使用任何泛型],即使那个泛型是类型参数而且确实应该是最专业的选择。
所以,似乎要解决这个问题,正如@VMAtm 所建议的那样,您应该将 equalTo
之类的方法添加到 class 中,以便实际方法在运行时间。