Хранение объектов, соответствующих протоколу с обобщениями, в типизированном массиве - PullRequest
0 голосов
/ 23 февраля 2019

У меня есть протокол:

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

Но это, конечно, не работает, с ошибкой:

ошибка: протокол «Настраиваемый» может использоваться толькокак общее ограничение, потому что оно имеет Self или связанные требования к типу. var varSet: [Adjustable] = []

Оглядываясь вокруг, я сначала подумал, что это потому, что протокол не соответствует 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)

Ответы [ 2 ]

0 голосов
/ 23 февраля 2019

добились большого прогресса, используя предложение Александра;Я смог использовать несколько вложенных классов для наследования класса стирания базового типа и использовать общий протокол, соответствующий 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, в которой приведен простой и понятный пример реализации ластика универсального типа.

0 голосов
/ 23 февраля 2019

Вы не можете иметь массив элементов типа Adjustable, потому что Adjustable на самом деле не тип.Это синяя печать, которая описывает набор типов, по одному на каждое возможное значение T.

Чтобы обойти это, вам нужно использовать ластик типов https://medium.com/dunnhumby-data-science-engineering/swift-associated-type-design-patterns-6c56c5b0a73a

...