首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在类型化数组中使用泛型存储符合协议的对象。

在类型化数组中使用泛型存储符合协议的对象。
EN

Stack Overflow用户
提问于 2019-02-22 22:29:56
回答 3查看 107关注 0票数 3

我有个规矩:

代码语言:javascript
复制
protocol Adjustable: Equatable {
    associatedtype T
    var id: String { get set }
    var value: T { get set }
    init(id: String, value: T)
}

以及符合它的结构:

代码语言:javascript
复制
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的包装类来处理这些属性的有序列表:

代码语言:javascript
复制
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))

但是,这当然是行不通的,错误的地方有:

错误:协议‘可调整’只能用作通用约束,因为它具有自或关联类型的需求变量adjustmentSet:可调整= []

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

此外,我希望能够在这里使用泛型,这样我就可以做如下的事情:

代码语言:javascript
复制
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)

或者:

代码语言:javascript
复制
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]类型的数组,这样我最终可以做到:

代码语言:javascript
复制
var set = AdjustmentSet()
if set.contains(.brightness) {
    // Do something!
}

代码语言:javascript
复制
var brightness = ...
brightness.value = 1.5
set.append(.brightness)
EN

回答 3

Stack Overflow用户

发布于 2019-02-22 22:49:31

您不能拥有一个类型为Adjustable的项数组,因为Adjustable实际上不是一个类型。这是一个描述一组类型的蓝图,每一个可能的T值都有一个。

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

票数 2
EN

Stack Overflow用户

发布于 2019-02-23 06:13:58

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

代码语言:javascript
复制
// 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)
    }
}

打印:

代码语言:javascript
复制
Set<AnyHashable>
true
0.0

特别感谢本文:dus/type-erasure-in-swift-84480c807534,它有一个关于如何实现泛型类型橡皮擦的简单而干净的示例。

票数 1
EN

Stack Overflow用户

发布于 2022-09-07 16:07:29

使用SWIFT5.7,您可以使用any作为协议的前缀,从而避免编译器的任何错误,因此您的集合如下:

代码语言:javascript
复制
struct AdjustmentSet {
    var adjustmentSet: [any Adjustable] = []
    func contains(_ item: some Adjustable) -> Bool {
        return adjustmentSet.first { $0.id == item.id } != nil
    }
}

请注意,adjustmentSet数组中的所有项都将在堆上分配,因为编译时Adjustable不能确定存在类型Adjustable的大小,因为实现它的类型将具有可变大小。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54836085

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档