代码之家  ›  专栏  ›  技术社区  ›  brandonscript

使用开关控制流从函数推断返回类型

  •  0
  • brandonscript  · 技术社区  · 5 年前

    我有一套属性/协议(背景是 here ,但我认为这是多余的)

    类类型如下:

    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 ,我已经构建了一些助手方法来 工具更简单:

    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()
    

    我现在要做的是在我的 管理类能够检索具有正确类型的属性,例如,如果我这样做:

    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()
    
    0 回复  |  直到 5 年前
        1
  •  2
  •   l_priebe    5 年前

    您需要将该方法重写为泛型,并使用足够的类型信息调用它,即您需要提前知道您希望该方法返回的类型。

    我也不确定传递任何哈希表是否理想。没有什么可以阻止您添加字符串、int和其他可散列到调整集的随机类型。

    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()方法也应该是强类型的,因为您不会传递任何哈希表我实现了这样的亮度和白平衡:

    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
    

    希望这有帮助,祝你的项目好运!