如何强制使用协议默认扩展 "override"?
How to force usage of protocol default extension "override"?
我有一个 PAT 协议,它在扩展中提供了默认实现。
然后,符合该协议的 class 提供“覆盖”。
不会调用此覆盖,编译器“更喜欢”默认值。
//---- Definition of the protocol with a default implentation, because I am forced to.
protocol Bag: AnyObject {
associatedtype BagObject
func add(_ e: BagObject)
}
extension Bag where BagObject: Equatable {
// I here give a default implementation, because I can't do differently, as this
// is an extension, and it is an extension because it only applies to assoicated
// types that are Equatables
func contains(_ e: BagObject) -> Bool {
print("Default implementation is called")
return false
}
}
///---- Definition of a class that gives a concrete implementation of the protocol
class BagConcreteImplementation<BagObject>: Bag {
var bag = Array<BagObject>()
func add(_ e: BagObject) { bag.append(e) }
}
extension BagConcreteImplementation where BagObject: Equatable {
// I here give a concrete implementation when the generic type is Equatable
func contains(_ e: BagObject) -> Bool {
print("Concrete implementation is called")
return bag.contains(e)
}
}
///---- This is a class that encapsulate a bag, in real life, this class is adding some filtering to objects that can be added to the bag
class AClassThatHaveABag<BagType: Bag> {
typealias BagObject = BagType.BagObject
let myBag: BagType
init(bag: BagType) { myBag = bag }
}
extension AClassThatHaveABag where BagType.BagObject: Equatable {
func contains(_ e: BagObject) {
// The problem here is that the compiler only knows that myBag is a Bag
// of Int and therefore calls the default implementation
// It does not call the concrete implementation of the concrete class that WILL be provided
myBag.contains(e)
}
}
let aBagOfInt = BagConcreteImplementation<Int>()
let objectThatContainsABagOfInt = AClassThatHaveABag(bag: aBagOfInt)
aBagOfInt.contains(0)
objectThatContainsABagOfInt.contains(0)
// Prints
// Concrete implementation is called
// Default implementation is called
我们可以清楚的看到直接调用是调用具体实现,而封装调用是调用默认实现
如何确保始终调用具体实现,即使是通过封装?
我尝试了一些方法,不知何故这有效:
在协议中声明 contains
,而不是在扩展中,如下所示:
func contains<T>(_ e: T) -> Bool where T: Equatable, T == BagObject
如果您在 class 中执行 T == BagObject
之类的操作,将出现编译器错误,指出这会使 T
变得多余,但显然这在协议中是可以的。
然后,像这样实现默认实现:
extension Bag {
func contains<T>(_ e: T) -> Bool where T: Equatable, T == BagObject {
print("Default implementation is called")
return false
}
}
然后,您可以直接在class中实现具体实现,作为非泛型方法。
class BagConcreteImplementation<BagObject> : Bag {
func contains(_ e: BagObject) -> Bool where BagObject : Equatable {
print("Concrete implementation is called")
return bag.contains(e)
}
var bag = Array<BagObject>()
func add(_ e: BagObject) { bag.append(e) }
}
在不更改调用站点的情况下,代码将打印:
Concrete implementation is called
Concrete implementation is called
为了完整起见,我 post 这里是另一个解决方案,它避免使用 T == BagObject
我发现(我的愚见)不是那么干净。
想法是使用 Bag
的特殊版本,称为 BagOfEquatables
,并扩展 类
protocol Bag: AnyObject {
associatedtype BagObject
func add(_ e: BagObject)
}
// Specialized version of Bag for Equatables
protocol BagOfEquatables: Bag where BagObject: Equatable{
func contains(_ e: BagObject) -> Bool
}
extension BagOfEquatables {
func contains(_ e: BagObject) -> Bool {
print("Default implementation is called")
return false
}
}
///---- Definition of a class that gives a concrete implementation of the protocol
class BagConcreteImplementation<BagObject>: Bag {
var bag = Array<BagObject>()
func add(_ e: BagObject) { bag.append(e) }
}
extension BagConcreteImplementation: BagOfEquatables where BagObject: Equatable {
func contains(_ e: BagObject) -> Bool {
print("Concrete implementation is called")
return bag.contains(e)
}
}
///---- This is a class that encapsulate a bag
class AClassThatHaveABag<BagType: Bag> {
typealias BagObject = BagType.BagObject
let myBag: BagType
init(bag: BagType) { myBag = bag }
}
extension AClassThatHaveABag where BagType: BagOfEquatables {
func contains(_ e: BagObject) -> Bool {
myBag.contains(e)
}
}
let aBagOfInt = BagConcreteImplementation<Int>()
let objectThatContainsABagOfInt = AClassThatHaveABag(bag: aBagOfInt)
aBagOfInt.contains(0)
objectThatContainsABagOfInt.contains(0)
// Prints
// Concrete implementation is called
// Concrete implementation is called
你说:
This override is not called, and the compiler "prefers" the default.
问题是您在扩展中提供了该方法的默认实现,但从未将其声明为协议的一部分。因此,编译器将使用“静态分派”。
如果您在协议中包含该方法,编译器将使用“动态调度”来决定调用哪个方法。
有关静态与动态调度的讨论,请参阅 WWDC 2016 Understanding Swift Performance。
例如:
protocol Foo {
// without this declaration, this will use static dispatch;
// uncomment the following line for dynamic dispatch
// func foo()
}
extension Foo {
func foo() {
print("default")
}
}
struct Bar: Foo {
func foo() {
print("bar implementation")
}
}
let bar: Foo = Bar()
bar.foo() // “default” with static dispatch; ”bar implementation“ with dynamic dispatch
我有一个 PAT 协议,它在扩展中提供了默认实现。 然后,符合该协议的 class 提供“覆盖”。 不会调用此覆盖,编译器“更喜欢”默认值。
//---- Definition of the protocol with a default implentation, because I am forced to.
protocol Bag: AnyObject {
associatedtype BagObject
func add(_ e: BagObject)
}
extension Bag where BagObject: Equatable {
// I here give a default implementation, because I can't do differently, as this
// is an extension, and it is an extension because it only applies to assoicated
// types that are Equatables
func contains(_ e: BagObject) -> Bool {
print("Default implementation is called")
return false
}
}
///---- Definition of a class that gives a concrete implementation of the protocol
class BagConcreteImplementation<BagObject>: Bag {
var bag = Array<BagObject>()
func add(_ e: BagObject) { bag.append(e) }
}
extension BagConcreteImplementation where BagObject: Equatable {
// I here give a concrete implementation when the generic type is Equatable
func contains(_ e: BagObject) -> Bool {
print("Concrete implementation is called")
return bag.contains(e)
}
}
///---- This is a class that encapsulate a bag, in real life, this class is adding some filtering to objects that can be added to the bag
class AClassThatHaveABag<BagType: Bag> {
typealias BagObject = BagType.BagObject
let myBag: BagType
init(bag: BagType) { myBag = bag }
}
extension AClassThatHaveABag where BagType.BagObject: Equatable {
func contains(_ e: BagObject) {
// The problem here is that the compiler only knows that myBag is a Bag
// of Int and therefore calls the default implementation
// It does not call the concrete implementation of the concrete class that WILL be provided
myBag.contains(e)
}
}
let aBagOfInt = BagConcreteImplementation<Int>()
let objectThatContainsABagOfInt = AClassThatHaveABag(bag: aBagOfInt)
aBagOfInt.contains(0)
objectThatContainsABagOfInt.contains(0)
// Prints
// Concrete implementation is called
// Default implementation is called
我们可以清楚的看到直接调用是调用具体实现,而封装调用是调用默认实现
如何确保始终调用具体实现,即使是通过封装?
我尝试了一些方法,不知何故这有效:
在协议中声明 contains
,而不是在扩展中,如下所示:
func contains<T>(_ e: T) -> Bool where T: Equatable, T == BagObject
如果您在 class 中执行 T == BagObject
之类的操作,将出现编译器错误,指出这会使 T
变得多余,但显然这在协议中是可以的。
然后,像这样实现默认实现:
extension Bag {
func contains<T>(_ e: T) -> Bool where T: Equatable, T == BagObject {
print("Default implementation is called")
return false
}
}
然后,您可以直接在class中实现具体实现,作为非泛型方法。
class BagConcreteImplementation<BagObject> : Bag {
func contains(_ e: BagObject) -> Bool where BagObject : Equatable {
print("Concrete implementation is called")
return bag.contains(e)
}
var bag = Array<BagObject>()
func add(_ e: BagObject) { bag.append(e) }
}
在不更改调用站点的情况下,代码将打印:
Concrete implementation is called
Concrete implementation is called
为了完整起见,我 post 这里是另一个解决方案,它避免使用 T == BagObject
我发现(我的愚见)不是那么干净。
想法是使用 Bag
的特殊版本,称为 BagOfEquatables
,并扩展 类
protocol Bag: AnyObject {
associatedtype BagObject
func add(_ e: BagObject)
}
// Specialized version of Bag for Equatables
protocol BagOfEquatables: Bag where BagObject: Equatable{
func contains(_ e: BagObject) -> Bool
}
extension BagOfEquatables {
func contains(_ e: BagObject) -> Bool {
print("Default implementation is called")
return false
}
}
///---- Definition of a class that gives a concrete implementation of the protocol
class BagConcreteImplementation<BagObject>: Bag {
var bag = Array<BagObject>()
func add(_ e: BagObject) { bag.append(e) }
}
extension BagConcreteImplementation: BagOfEquatables where BagObject: Equatable {
func contains(_ e: BagObject) -> Bool {
print("Concrete implementation is called")
return bag.contains(e)
}
}
///---- This is a class that encapsulate a bag
class AClassThatHaveABag<BagType: Bag> {
typealias BagObject = BagType.BagObject
let myBag: BagType
init(bag: BagType) { myBag = bag }
}
extension AClassThatHaveABag where BagType: BagOfEquatables {
func contains(_ e: BagObject) -> Bool {
myBag.contains(e)
}
}
let aBagOfInt = BagConcreteImplementation<Int>()
let objectThatContainsABagOfInt = AClassThatHaveABag(bag: aBagOfInt)
aBagOfInt.contains(0)
objectThatContainsABagOfInt.contains(0)
// Prints
// Concrete implementation is called
// Concrete implementation is called
你说:
This override is not called, and the compiler "prefers" the default.
问题是您在扩展中提供了该方法的默认实现,但从未将其声明为协议的一部分。因此,编译器将使用“静态分派”。
如果您在协议中包含该方法,编译器将使用“动态调度”来决定调用哪个方法。
有关静态与动态调度的讨论,请参阅 WWDC 2016 Understanding Swift Performance。
例如:
protocol Foo {
// without this declaration, this will use static dispatch;
// uncomment the following line for dynamic dispatch
// func foo()
}
extension Foo {
func foo() {
print("default")
}
}
struct Bar: Foo {
func foo() {
print("bar implementation")
}
}
let bar: Foo = Bar()
bar.foo() // “default” with static dispatch; ”bar implementation“ with dynamic dispatch