Могу ли я создать перечисление Swift generi c, чтобы использовать его регистры для вывода типа для класса generi c? - PullRequest
2 голосов
/ 25 февраля 2020

Могу ли я создать enum generi c (разные типы для каждого случая), чтобы я мог использовать его случаи, чтобы вывести тип для класса generi c?

У меня есть пример здесь:

class Config {
    let name: String
    init(_ name: String) {
        self.name = name
    }
}

class CarConfig: Config {
    static let sports = CarConfig("sports")
    static let van = CarConfig("van")
}

class BikeConfig: Config {
    static let road = BikeConfig("road")
    static let mtb = BikeConfig("mtb")
}

enum VehicleType {
    case car, bike, scooter
}

class Vehicle<C: Config> {

    let type: VehicleType
    let config: C

    init(type: VehicleType, config: C) {
        self.type = type
        self.config = config
    }

}

let bike = Vehicle(type: .bike, config: BikeConfig.mtb)

bike.config.name // mtb

То, что я хотел бы сделать, - это запустить транспортное средство следующим образом:

let bike = Vehicle(type: .bike, config: .mtb)

Я хочу, чтобы компилятор выводил BikeConfig, чтобы я мог его опустить , Я хочу, чтобы компилятор знал, что Vehicle с type == VehicleType.bike всегда имеет Config, то есть BikeConfig.

Мне бы пришлось изменить Vehicle, очевидно:

class Vehicle<C: Config> {

    let type: VehicleType<C>
    let config: C

    init(type: VehicleType<C>, config: C) {
        self.type = type
        self.config = config
    }

}

А теперь сделайте enum VehicleType enum VehicleType<C: Config>.

Не знаю, откуда отсюда go. Любая помощь? :)

[ОБНОВЛЕНИЕ: добавлено scooter кейс для VehicleType]

Ответы [ 2 ]

4 голосов
/ 25 февраля 2020

Вы можете использовать Связанные значения с корпусами enum в качестве любого типа.

Создать enum VehicleType с case car с CarConfig и case bike с BikeConfig как связанных типов ,

    enum VehicleType {
        case car(CarConfig)
        case bike(BikeConfig)
    }

Теперь определение для class Vehicle можно изменить на

    class Vehicle {
        let type: VehicleType
        init(type: VehicleType) {
            self.type = type
        }
    }

Создать экземпляр Vehicle с помощью,

    let bike = Vehicle(type: .bike(.mtb))

2 голосов
/ 25 февраля 2020

Вы получаете его задом наперед, вам не следует выводить тип generi c в зависимости от значения перечисления, поскольку это означает, что вы хотите определить некоторую вещь времени компиляции (тип generi c) со значением, которое возможно известен во время выполнения (значение перечисления).

Поэтому нам нужно сделать параметр type доступным только во время компиляции, то есть также параметром типа.

Сначала вы вводите VehicleTypeProtocol и struct, реализующие этот протокол для каждого случая перечисления:

protocol VehicleTypeProtocol {
    // this associated type creates the link between a vehicle type and a config type
    associatedtype ConfigType: Config
    // this is so that we can assign to Vehicle.type
    static var type: VehicleType { get }
}

struct Car : VehicleTypeProtocol {
    typealias ConfigType = CarConfig
    static var type: VehicleType { .car }
}

struct Bike : VehicleTypeProtocol {
    typealias ConfigType = BikeConfig
    static var type: VehicleType { .bike }
}

struct Scooter: VehicleTypeProtocol {
    typealias ConfigType = BikeConfig
    static var type: VehicleType { .scooter }
}

И затем инициализатор может быть реализован так:

init<T: VehicleTypeProtocol>(type: T.Type, config: C) where T.ConfigType == C {
    self.type = T.type
    self.config = config
}

Использование:

let bike = Vehicle(type: Bike.self, config: .mtb)

Но, мужик, это запутанно ...

...