Swift:嵌套类型擦除

沙j

使用Swift 3.0(如果可以,我可以使用Swift 4.0 ...但是我认为不会)我想键入Erase两个级别。我要键入什么来擦除具有关联类型的协议,该协议符合本身又具有关联类型的协议。所以可以说我要键入擦除嵌套的associatedtypes。

下面的代码是我的代码的极其简化的版本,但这种方式更加清晰。所以我真正想要的是这样的东西:

原始方案-未解决

protocol Motor {
    var power: Int { get } 
}

protocol Vehicle {
    associatedType Engine: Motor
    var engine: Engine { get }
}

protocol Transportation {
    associatedType Transport: Vehicle
    var transport: Transport { get }
}

然后我想键入擦除Transportation,并能够存储阵列AnyTransportation可能有任何Vehicle这反过来又可以有什么Motor

因此,这是一个具有3种协议的场景,其中2种具有(嵌套)关联类型。

我不知道该怎么做。实际上,我什至不知道如何解决更简单的情况:

简化方案-未解决

我们可以将上面的原始场景简化为具有2种协议的版本,其中只有1种具有associatedtype:

protocol Vehicle {
    var speed: Int { get }
}

protocol Transportation {
    associatedtype Transport: Vehicle
    var transport: Transport { get }
    var name: String { get }
}

然后说我们Bus符合Vehicle

struct Bus: Vehicle {
    var speed: Int { return 60 }
}

然后我们有两个不同的BusLineRedBusLine并且BlueBusLine都符合Transportation

struct RedBusLine: Transportation {
    let transport: Bus
    var name = "Red line"
    init(transport: Bus = Bus()) {
        self.transport = transport
    }
}

struct BlueBusLine: Transportation {
    let transport: Bus
    var name = "Blue line"
    init(transport: Bus = Bus()) {
        self.transport = transport
    }
}

然后,我们可以Transportation使用基本模式和框模式以及类来键入“擦除” ,如bignerdranch所述

final class AnyTransportation<_Transport: Vehicle>: Transportation {
    typealias Transport = _Transport
    private let box: _AnyTransportationBase<Transport>
    init<Concrete: Transportation>(_ concrete: Concrete) where Concrete.Transport == Transport {
        box = _AnyTransportationBox(concrete)
    }
    init(transport: Transport) { fatalError("Use type erasing init instead") }
    var transport: Transport { return box.transport }
    var name: String { return box.name }
}

final class _AnyTransportationBox<Concrete: Transportation>: _AnyTransportationBase<Concrete.Transport> {
    private let concrete: Concrete
    init(_ concrete: Concrete) { self.concrete = concrete; super.init() }
    required init(transport: Transport) { fatalError("Use type erasing init instead") }
    override var transport: Transport { return concrete.transport }
    override var name: String {return concrete.name }
}

class _AnyTransportationBase<_Transport: Vehicle> : Transportation {
    typealias Transport = _Transport
    init() { if type(of: self) == _AnyTransportationBase.self { fatalError("Use Box class") } }
    required init(transport: Transport) { fatalError("Use type erasing init instead") }
    var transport: Transport { fatalError("abstract") }
    var name: String { fatalError("abstract") }
}

然后,我们可以把两种RedBusLineBlueBusLine

let busRides: [AnyTransportation<Bus>] = [AnyTransportation(RedBusLine()), AnyTransportation(BlueBusLine())]
busRides.forEach { print($0.name) } // prints "Red line\nBlue line"

在上面链接到有关类型擦除的博客文章中,我想要的实际上是的解决方法Homogeneous Requirement

想象我们还有另一个Vehicle,例如aFerry和a FerryLine

struct Ferry: Vehicle {
    var speed: Int { return 40 }
}

struct FerryLine: Transportation {
    let transport: Ferry = Ferry()
    var name = "Ferry line"
}

我想我们Vehicle现在要输入擦除因为我们想要一个数组AnyTransportation<AnyVehicle>,对吗?

final class AnyVehicle: Vehicle {
    private let box: _AnyVehicleBase
    init<Concrete: Vehicle>(_ concrete: Concrete) {
        box = _AnyVehicleBox(concrete)
    }
    var speed: Int { return box.speed }
}

final class _AnyVehicleBox<Concrete: Vehicle>: _AnyVehicleBase {
    private let concrete: Concrete
    init(_ concrete: Concrete) { self.concrete = concrete; super.init() }
    override var speed: Int { return concrete.speed }
}

class _AnyVehicleBase: Vehicle {
    init() { if type(of: self) == _AnyVehicleBase.self { fatalError("Use Box class") } }
    var speed: Int { fatalError("abstract") }
}

// THIS DOES NOT WORK
let rides: [AnyTransportation<AnyVehicle>] = [AnyTransportation(AnyVehicle(RedBusLine())), AnyTransportation(AnyVehicle(FerryLine()))] // COMPILE ERROR: error: argument type 'RedBusLine' does not conform to expected type 'Vehicle'

当然,这是行不通的……因为AnyTransportation希望传递一个符合的类型Transportation,但AnyVehicle当然不符合它。

但是我还没有找到解决方案。有没有?

问题1:是否可以键入擦除简单方案,以实现以下目的[AnyTransportation<AnyVehicle>]

问题2:如果简单方案可以解决,那么原始方案是否也可以解决?

以下仅是我想通过原始方案实现的目标的更详细说明

原始方案-扩展

我最初的需求是将Transportation具有任何any的Vehicle,本身具有Motor同一个数组内的任意一个放入:

let transportations: [AnyTransportation<AnyVehicle<AnyMotor>>] = [BusLine(), FerryLine()] // want to put `BusLine` and `FerryLine` in same array
哈米什

如果要使用任何配备任何发动机的车辆来表示任何运输,则需要3个盒子,每个盒子都用“以前的”类型擦除包装纸来表示。您不希望在任何这些框上使用通用占位符,因为您要针对完全异构的实例进行交谈(例如,没有任何特定 Vehicle类型的运输工具或任何特定 Motor类型的车辆)。

此外,您可以使用闭包代替使用闭包,而不是使用类层次结构来执行类型擦除,闭包允许您捕获基本实例,而不是直接存储它。这使您可以从原始代码中删除大量的样板。

例如:

protocol Motor {
    var power: Int { get }
}

protocol Vehicle {
    associatedtype Engine : Motor
    var engine: Engine { get }
}

protocol Transportation {
    associatedtype Transport : Vehicle
    var transport: Transport { get }
    var name: String { get set }
}

// we need the concrete AnyMotor wrapper, as Motor is not a type that conforms to Motor
// (as protocols don't conform to themselves).
struct AnyMotor : Motor {

    // we can store base directly, as Motor has no associated types.
    private let base: Motor

    // protocol requirement just forwards onto the base.
    var power: Int { return base.power }

    init(_ base: Motor) {
        self.base = base
    }
}

struct AnyVehicle : Vehicle {

    // we cannot directly store base (as Vehicle has an associated type). 
    // however we can *capture* base in a closure that returns the value of the property,
    // wrapped in its type eraser.
    private let _getEngine: () -> AnyMotor

    var engine: AnyMotor { return _getEngine() }

    init<Base : Vehicle>(_ base: Base) {
        self._getEngine = { AnyMotor(base.engine) }
    }
}

struct AnyTransportation : Transportation {

    private let _getTransport: () -> AnyVehicle
    private let _getName: () -> String
    private let _setName: (String) -> Void

    var transport: AnyVehicle { return _getTransport() }
    var name: String {
        get { return _getName() }
        set { _setName(newValue) }
    }

    init<Base : Transportation>(_ base: Base) {
        // similar pattern as above, just multiple stored closures.
        // however in this case, as we have a mutable protocol requirement,
        // we first create a mutable copy of base, then have all closures capture
        // this mutable variable.
        var base = base
        self._getTransport = { AnyVehicle(base.transport) }
        self._getName = { base.name }
        self._setName = { base.name = $0 }
    }
}

struct PetrolEngine : Motor {
    var power: Int
}

struct Ferry: Vehicle {
    var engine = PetrolEngine(power: 100)
}

struct FerryLine: Transportation {
    let transport = Ferry()
    var name = "Ferry line"
}

var anyTransportation = AnyTransportation(FerryLine())

print(anyTransportation.name) // Ferry line
print(anyTransportation.transport.engine.power) // 100

anyTransportation.name = "Foo bar ferries"
print(anyTransportation.name) // Foo bar ferries

请注意,AnyMotor尽管Motor没有任何关联的类型,我们仍在构建这是因为协议不符合其自身,所以我们不能使用Motor其自身来满足Engine关联的类型(需要: Motor)-我们目前必须为其构建一个具体的包装器类型。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章