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

在类型化数组中存储符合带有泛型的协议的对象

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

    我有个协议:

    protocol Adjustable: Equatable {
        associatedtype T
        var id: String { get set }
        var value: T { get set }
        init(id: String, value: T)
    }
    

    以及符合它的结构:

    struct Adjustment: Adjustable {
        static func == (lhs: Adjustment, rhs: Adjustment) -> Bool {
            return lhs.id == rhs.id
        }
    
        typealias T = CGFloat
        var id: String
        var value: T
    }
    

    我正在构建一个包装类,它的行为类似于 Set 要处理这些属性的有序列表,请执行以下操作:

    struct AdjustmentSet {
        var adjustmentSet: [Adjustable] = []
        func contains<T: Adjustable>(_ item: T) -> Bool {
            return adjustmentSet.filter({ $0.id == item.id }).first != nil
        }
    }
    
    let brightness = Adjustment(id: "Brightness", value: 0)
    
    let set = AdjustmentSet()
    print(set.contains(brightness))
    

    但那当然不行,错误是:

    错误:“可调整”协议只能用作泛型约束,因为它具有自身或关联的类型要求 变量调整集:[可调整]=[]

    环顾四周,我起初以为这是因为协议不符合 Equatable ,但后来我添加了它,它仍然不起作用(或者我做错了)。

    此外,我希望能够在这里使用泛型,以便我可以执行以下操作:

    struct Adjustment<T>: Adjustable {
        static func == (lhs: Adjustment, rhs: Adjustment) -> Bool {
            return lhs.id == rhs.id
        }
    
        var id: String
        var value: T
    }
    
    let brightness = Adjustment<CGFloat>(id: "Brightness", value: 0)
    

    或

    struct FloatAdjustment: Adjustable {
        static func == (lhs: Adjustment, rhs: Adjustment) -> Bool {
            return lhs.id == rhs.id
        }
        typealias T = CGFloat
        var id: String
        var value: T
    }
    
    let brightness = FloatAdjustment(id: "Brightness", value: 0)
    

    仍然可以存储 [Adjustable] 类型,这样我最终可以:

    var set = AdjustmentSet()
    if set.contains(.brightness) {
        // Do something!
    }
    

    或者

    var brightness = ...
    brightness.value = 1.5
    set.append(.brightness)
    
    1 回复  |  直到 7 年前
        1
  •  2
  •   Alexander    7 年前

    不能有类型为的项数组 Adjustable ,因为 可调 不是真正的类型。这是一个描述一组类型的蓝图,每一个可能的值 T 是的。

    要解决这个问题,你需要使用类型橡皮擦 https://medium.com/dunnhumby-data-science-engineering/swift-associated-type-design-patterns-6c56c5b0a73a

        2
  •  1
  •   brandonscript    7 年前

    使用Alexander的建议已经取得了很大的进展;我能够使用一些嵌套的类类型来继承基类型擦除类,并使用符合 AnyHashable 所以我可以用这个和一套!

    // 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()
    
    var orderedSet = Set<AnyHashable>()
    
    orderedSet.insert(brightness)
    print(type(of: orderedSet))
    print(orderedSet.contains(brightness))
    
    for obj in orderedSet {
        if let o = obj as? AdjustmentTypes.FloatType {
            print(o.value)
        }
        if let o = obj as? AdjustmentTypes.IntType {
            print(o.value)
        }
    }
    

    印刷品:

    Set<AnyHashable>
    true
    0.0
    

    特别感谢这篇文章: https://medium.com/@chris_dus/type-erasure-in-swift-84480c807534 其中有一个关于如何实现泛型类型擦除器的简单而干净的示例。