Вывести тип возврата из функции с помощью переключателя управления потоком - PullRequest
0 голосов
/ 23 февраля 2019

У меня есть набор свойств / протоколов (история здесь , но я думаю, что это излишне)

Типы классов выглядят так:

struct AdjustmentTypes {
    internal class BaseType<T>: Hashable {

        static func == (lhs: AdjustmentTypes.BaseType<T>, rhs: AdjustmentTypes.BaseType<T>) -> Bool {
            return lhs.name == rhs.name
        }

        typealias A = T

        var hashValue: Int { return name.hashValue }

        let name: String
        let defaultValue: T
        let min: T
        let max: T
        var value: T

        init(name: String, defaultValue: T, min: T, max: T) {
            self.name = name
            self.defaultValue = defaultValue
            self.min = min
            self.max = max
            self.value = defaultValue
        }
    }

    class FloatType: BaseType<CGFloat> { }

    class IntType: BaseType<Int> { }
}

И я использую стирание типа для удаления типа, чтобы я мог сохранить их в Set, и я создал несколько вспомогательных методов, чтобы упростить мой инструмент Set:

class AdjustmentsSet {

    private var adjustmentsSet: Set<AnyHashable> = []

    func insert(_ adjustment: AnyHashable) {
        adjustmentsSet.insert(adjustment)
    }

    func remove(_ adjustment: AnyHashable) {
        adjustmentsSet.remove(adjustment)
    }

    func contains(_ adjustment: AnyHashable) -> Bool {
        return adjustmentsSet.contains(adjustment)
    }

    var count: Int { return adjustmentsSet.count }
}

var adjustmentsSet = AdjustmentsSet()

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

let brightness = Brightness().make()
adjustments.get(brightness)

Оно должно вернуть nil, но если я сделаю:

adjustments.insert(brightness)
adjustments.get(brightness)

Теперь я должен получить значение обратно, как его правильный тип, AdjustmentTypes.FloatType.

Я думаю, что-то с SwitchУтверждение, подобное этому:

class AdjustmentsSet {

    // ...

    func get(_ adjustment: AnyHashable) -> Any? {
        guard let untyped = adjustmentsSet.first(where: { $0 == adjustment }) else { return nil }
        switch adjustment {
        case _ as AdjustmentTypes.FloatType: return untyped as! AdjustmentTypes.FloatType
        case _ as AdjustmentTypes.IntType: return untyped as! AdjustmentTypes.IntType
        default: return nil
        }
    }
}

Однако, фатальный недостаток, конечно, заключается в том, что это возвращает Any вместо предполагаемого типа.

Как я могу вывести возвраттип значения и чтобы вернуть правильный тип?


Полный пример, просто бросьте это на игровую площадку:

// Generic conforming protocol to AnyHashable
protocol AnyAdjustmentProtocol {
    func make() -> AnyHashable
}

protocol AdjustmentProtocol: AnyAdjustmentProtocol {
    associatedtype A
    func make() -> A
}

struct AdjustmentTypes {
    internal class BaseType<T>: Hashable {

        static func == (lhs: AdjustmentTypes.BaseType<T>, rhs: AdjustmentTypes.BaseType<T>) -> Bool {
            return lhs.name == rhs.name
        }

        typealias A = T

        var hashValue: Int { return name.hashValue }

        let name: String
        let defaultValue: T
        let min: T
        let max: T
        var value: T

        init(name: String, defaultValue: T, min: T, max: T) {
            self.name = name
            self.defaultValue = defaultValue
            self.min = min
            self.max = max
            self.value = defaultValue
        }
    }

    class FloatType: BaseType<CGFloat> { }

    class IntType: BaseType<Int> { }
}

struct AnyAdjustmentType<A>: AdjustmentProtocol, Hashable {
    static func == (lhs: AnyAdjustmentType<A>, rhs: AnyAdjustmentType<A>) -> Bool {
        return lhs.hashValue == rhs.hashValue
    }

    private let _make: () -> AnyHashable
    private let hashClosure:() -> Int

    var hashValue: Int {
        return hashClosure()
    }

    init<T: AdjustmentProtocol & Hashable>(_ adjustment: T) where T.A == A {
        _make = adjustment.make
        hashClosure = { return adjustment.hashValue }
    }
    func make() -> AnyHashable {
        return _make()
    }
}

struct Brightness: AdjustmentProtocol, Hashable {
    func make() -> AnyHashable {
        return AdjustmentTypes.FloatType(name: "Brightness", defaultValue: 0, min: 0, max: 1)
    }
}
struct WhiteBalance: AdjustmentProtocol, Hashable {
    func make() -> AnyHashable {
        return AdjustmentTypes.IntType(name: "White Balance", defaultValue: 4000, min: 3000, max: 7000)
    }
}

let brightness = Brightness().make()
let whiteBalance = WhiteBalance().make()

class AdjustmentsSet {

    private var adjustmentsSet: Set<AnyHashable> = []

    func insert(_ adjustment: AnyHashable) {
        adjustmentsSet.insert(adjustment)
    }

    func remove(_ adjustment: AnyHashable) {
        adjustmentsSet.remove(adjustment)
    }

    func contains(_ adjustment: AnyHashable) -> Bool {
        return adjustmentsSet.contains(adjustment)
    }

    var count: Int { return adjustmentsSet.count }
}

var adjustmentsSet = AdjustmentsSet()

1 Ответ

0 голосов
/ 24 февраля 2019

Вам нужно переписать метод как универсальный и вызвать его с достаточной информацией о типе, т.е. вам нужно заранее знать, какой тип вы ожидаете, что метод вернет.

Я также не уверен, что обойдусьAnyHashables идеально подходит в любом случае.Ничто не мешает вам добавлять строки, целые и другие случайные типы, которые можно хэшировать в вашем наборе корректировок.

var adjustmentsSet = AdjustmentsSet()
adjustmentsSet.insert("1") // compiles just fine!

В качестве альтернативы, вы можете использовать и передавать ваши AdjustmentTypes и переписывать класс AdjustmentsSet с помощью универсальных методов:

class AdjustmentsSet {

    private var adjustmentsSet: Set<AnyHashable> = []

    func insert<T>(_ adjustment: AdjustmentTypes.BaseType<T>) {
        adjustmentsSet.insert(adjustment)
    }

    func remove<T>(_ adjustment: AdjustmentTypes.BaseType<T>) {
        adjustmentsSet.remove(adjustment)
    }

    func contains<T>(_ adjustment: AdjustmentTypes.BaseType<T>) -> Bool {
        return adjustmentsSet.contains(adjustment)
    }

    func get<T>(_ adjustment: AdjustmentTypes.BaseType<T>) -> AdjustmentTypes.BaseType<T>? {
        return (adjustmentsSet.compactMap { $0 as? AdjustmentTypes.BaseType<T> }).first(where: { $0 == adjustment })
    }

    var count: Int { return adjustmentsSet.count }
}

Далее, ваши методы make () также должны быть более строго типизированы, поскольку вы не передаете AnyHashables.Я реализовал яркость и баланс белого следующим образом:

extension AdjustmentTypes {
    static let Brightness = AdjustmentTypes.FloatType(name: "Brightness", defaultValue: 0, min: 0, max: 1)
    static let WhiteBalance = AdjustmentTypes.IntType(name: "White Balance", defaultValue: 4000, min: 3000, max: 7000)
}

А также использовал преимущества псевдонимов и структур типов в Swift, чтобы ваша система типов настройки работала с семантикой значений:

struct AdjustmentTypes {

    struct BaseType<T>: Hashable {

        static func == (lhs: AdjustmentTypes.BaseType<T>, rhs: AdjustmentTypes.BaseType<T>) -> Bool {
            return lhs.name == rhs.name
        }

        typealias A = T

        var hashValue: Int { return name.hashValue }

        let name: String
        let defaultValue: T
        let min: T
        let max: T
        var value: T

        init(name: String, defaultValue: T, min: T, max: T) {
            self.name = name
            self.defaultValue = defaultValue
            self.min = min
            self.max = max
            self.value = defaultValue
        }
    }

    typealias FloatType = BaseType<CGFloat>
    typealias IntType = BaseType<Int>
}

Наконец, вы можете использовать набор настроек, как предполагалось:

var brightness = AdjustmentTypes.Brightness
brightness.value = 0.5

var adjustmentsSet = AdjustmentsSet()
adjustmentsSet.insert(brightness)

let retrievedBrightness = adjustmentsSet.get(AdjustmentTypes.Brightness)! // strongly typed!
retrievedBrightness.value // 0.5
AdjustmentTypes.Brightness.value // 0.0

Вся игровая площадка:

struct AdjustmentTypes {

    struct BaseType<T>: Hashable {

        static func == (lhs: AdjustmentTypes.BaseType<T>, rhs: AdjustmentTypes.BaseType<T>) -> Bool {
            return lhs.name == rhs.name
        }

        typealias A = T

        var hashValue: Int { return name.hashValue }

        let name: String
        let defaultValue: T
        let min: T
        let max: T
        var value: T

        init(name: String, defaultValue: T, min: T, max: T) {
            self.name = name
            self.defaultValue = defaultValue
            self.min = min
            self.max = max
            self.value = defaultValue
        }
    }

    typealias FloatType = BaseType<CGFloat>
    typealias IntType = BaseType<Int>
}

extension AdjustmentTypes {
    static let Brightness = AdjustmentTypes.FloatType(name: "Brightness", defaultValue: 0, min: 0, max: 1)
    static let WhiteBalance = AdjustmentTypes.IntType(name: "White Balance", defaultValue: 4000, min: 3000, max: 7000)
}

class AdjustmentsSet {

    private var adjustmentsSet: Set<AnyHashable> = []

    func insert<T>(_ adjustment: AdjustmentTypes.BaseType<T>) {
        adjustmentsSet.insert(adjustment)
    }

    func remove<T>(_ adjustment: AdjustmentTypes.BaseType<T>) {
        adjustmentsSet.remove(adjustment)
    }

    func contains<T>(_ adjustment: AdjustmentTypes.BaseType<T>) -> Bool {
        return adjustmentsSet.contains(adjustment)
    }

    func get<T>(_ adjustment: AdjustmentTypes.BaseType<T>) -> AdjustmentTypes.BaseType<T>? {
        return (adjustmentsSet.compactMap { $0 as? AdjustmentTypes.BaseType<T> }).first(where: { $0 == adjustment })
    }

    var count: Int { return adjustmentsSet.count }
}

var brightness = AdjustmentTypes.Brightness
brightness.value = 0.5

var adjustmentsSet = AdjustmentsSet()
adjustmentsSet.insert(brightness)

let retrievedBrightness = adjustmentsSet.get(AdjustmentTypes.Brightness)! // strongly typed!
retrievedBrightness.value // 0.5
AdjustmentTypes.Brightness.value // 0.0

Надеюсь, это поможет, удачи в вашем проекте!

...