【问题标题】:Infer return type from function using switch control flow使用开关控制流从函数推断返回类型
【发布时间】:2019-02-23 20:38:16
【问题描述】:

我有一组属性/协议(背景是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 中,并且我构建了一些辅助方法来简化我的 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()

【问题讨论】:

    标签: ios swift generics casting


    【解决方案1】:

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

    我也不确定传递 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
    

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

    【讨论】:

    • 哦,这真是太棒了!这实际上也是我的下一步,所以你用一个烤饼喂了两只鸟?
    • 很高兴你发现它有用!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多