使用开关控制流程从函数推断返回类型 [英] Infer return type from function using switch control flow

查看:62
本文介绍了使用开关控制流程从函数推断返回类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一组属性/协议(背景故事在此处,但我认为这是多余的)

I've got a set of properties/protocols (back story is here, but I think it's superfluous)

类类型如下:

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 工具:

And I'm using type erasure to remove the type so I can store these in a Set, and I've built some helper methods to make my Set tooling simpler:

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

我现在要做的是在<$ c $中添加一些帮助器c>设置管理类,以便能够检索具有正确类型的属性,例如如果我这样做:

What I want to do now is add some helpers to my Set management class to be able to retrieve a property with the correct type, e.g. if I do:

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

应该返回 nil ,但是如果我这样做了:

It should return nil, but if I do:

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

我现在应该找回值,作为正确的类型 AdjustmentTypes.FloatType

I should now get the value back, as its correct type, AdjustmentTypes.FloatType.

我正在考虑使用 Switch 语句来执行以下操作:

I'm thinking to so something with a Switch statement like this:

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 ,而不是预期的类型。

However, the fatal flaw of course, is that this returns Any, instead of the intended type.

如何推断返回值的类型并返回正确的类型?

完整示例,只需将其放到操场上即可:

Complete example, just drop this into a playground:

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


推荐答案

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

You need to rewrite the method as a generic and call it with sufficient type information, i.e. you need to know in advance what type you expect the method to return.

我也不确定传递无论如何,AnyHashables是理想的。没有什么可以阻止您将可哈希化的字符串,整数和其他随机类型添加到调整集中的。

I'm also not sure passing around AnyHashables is ideal anyway. Nothing stops you from adding Strings, Ints and other random types that are hashable to your adjustment set.

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

或者,您可以使用并传递您的AdjustmentTypes并使用通用方法重写AdjustmentsSet类:

Alternatively, you could use and pass around your AdjustmentTypes and rewrite the AdjustmentsSet class with generic methods:

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,因此也应使用更强类型。我实现了亮度和白平衡,如下所示:

Next, your make() methods should be more strongly typed as well, since you are not passing around AnyHashables. I implemented brightness and white balance like this:

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中的类型别名和结构,使调整类型系统的行为带有值语义:

And also took advantage of type aliases and structs in Swift, to make your adjustment type system behave with value semantics:

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>
}

最后,您可以按预期使用调整集:

Finally, you are able to use the adjustment set as intended:

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

希望这会有所帮助,祝您的项目顺利!

Hope this helps, good luck with your project!

这篇关于使用开关控制流程从函数推断返回类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆