Swift 泛型:如何将关联类型与泛型参数匹配

Swift generics: how to match associatedtype with generic parameter

我的应用程序中有一个对象(比如一个用户对象),我想将其发布(因为某些 UI 依赖于它)。由于 Liskov 替换原则,我不想将它作为 EnvironmentObject 传递,所以我决定制作一个包装器。像这样:


protocol HasUser: ObservableObject {
    var user: String { get }
}

class HasUserWrapper: HasUser {
    var user: String { userGetter() }
    
    private let userGetter: () -> String
    
    let objectWillChange: AnyPublisher<Void, Never>
    
    init<UO: HasUser>(wrapping userObject: UO) {
        self.objectWillChange = userObject
            .objectWillChange
            .map { _ in () }
            .eraseToAnyPublisher()
        self.userGetter = { userObject.user }
    }
}

extension HasUser {
    func eraseToHasUserWrapper() -> HasUserWrapper {
        HasUserWrapper(wrapping: self)
    }
}

class ConcreteHasUser: HasUser {
    @Published var user: String = "john"
}

struct UserView {
    @ObservedObject var hasUser: HasUserWrapper = ConcreteHasUser().eraseToHasUserWrapper()
    var body: some View {
        Text(hasUser.user)
    }
}

现在,我想让这个包装器通用。所以我开始了这样的事情:

protocol HasThing: ObservableObject {
    associatedtype Thing
    var thing: Thing { get }
}

class HasThingWrapper<Thing>: HasThing {
    var thing: Thing { thingGetter() }
    private let thingGetter: () -> Foo

    let objectWillChange: AnyPublisher<Void, Never>

    init<HasThingType: HasThing>(wrapping hasThing: HasThingType)  {
        self.objectWillChange = hasThing
            .objectWillChange
            .map { _ in () }
            .eraseToAnyPublisher()
        self.thingGetter = { hasThing.  }
    }
}

extension HasThing {
    func eraseToThingWrapper() -> HasThingWrapper<Thing> {
        HasThingWrapper(wrapping: self)
    }
}

protocol HasString: HasThing {
    var thing: String { get }
}

class StringHolder: HasString {
    @Published var thing: String = "hello"
}

如何告诉编译器包装器中的通用类型 Thing 应该与 HasThing 协议中的关联类型 Thing 匹配?

如果我没理解错的话,你只需要在 init:

中添加一个泛型约束
init<HasThingType: HasThing>(wrapping hasThing: HasThingType)
    where HasThingType.Thing == Thing 
{
    ...
    // then you can do:
    self.thingGetter = { hasThing.thing }
}