Swift:使"shape"相同的两个类型符合一个共同的协议

Swift: Make two types with the same "shape" conform to a common protocol

我有两种不同的类型,它们表示相同的数据,并且具有完全相同的“形状”。这两种不同的类型是代码生成的,我不得不处理它们。但是,我想让它们符合一个共同的协议,这样我就可以对这两种类型一视同仁。这是一个例子:

假设这是我的两个代码生成类型,我坚持使用它们:

struct User1 {
    var email: String
    var name: Name
    
    struct Name {
        var givenName: String
        var familyName: String
    }
}

struct User2 {
    var email: String
    var name: Name
    
    struct Name {
        var givenName: String
        var familyName: String
    }
}

我希望能够互换使用这些类型,因此我创建了一些它们可以遵循的协议:

protocol NameRepresenting {
    var givenName: String { get }
    var familyName: String { get }
}

protocol UserRepresenting {
    var email: String { get }
    var name: NameRepresenting { get }
}

然后我试图让它们符合:

extension User1.Name: NameRepresenting {}
// Error: Type 'User1' does not conform to protocol 'UserRepresenting'
extension User1: UserRepresenting {}

extension User2.Name: NameRepresenting {}
// Error: Type 'User2' does not conform to protocol 'UserRepresenting'
extension User2: UserRepresenting {}

我希望上面的方法可以工作,但是编译失败并出现上面评论的错误。有什么优雅的方法可以让这两种类型符合通用协议,以便我可以互换使用它们吗?

生成的结构的 name 属性的类型为 Name,而不是协议要求的 NameRepresenting。 Swift 尚不支持协变 returns :(

您可以做的是添加关联类型要求:

protocol UserRepresenting {
    associatedtype Name : NameRepresenting
    var email: String { get }
    var name: Name { get }
}

这要求符合者具有符合NameRepresenting的类型并且是name属性.

的类型

但是,既然它有关联类型要求,您不能使用 UserRepresenting 作为 variable/function 参数的类型。您只能在通用约束中使用它。所以如果你有一个接受UserRepresenting的函数,你需要这样写:

func someFunction<UserType: UserRepresenting>(user: UserType) {

}

并且如果您的 classes/structs 之一需要存储类型 UserRepresenting 的 属性,您也需要使您的 class/struct 通用:

class Foo<UserType: UserRepresenting> {
    var someUser: UserType?
}

这可能适合也可能不适合您的情况。如果没有,你可以写一个类型橡皮擦:

struct AnyUserRepresenting : UserRepresenting {
    var email: String
    var name: Name
    struct Name : NameRepresenting {
        var givenName: String
        var familyName: String
    }

    init<UserType: UserRepresenting>(_ userRepresenting: UserType) {
        self.name = Name(
            givenName: userRepresenting.name.givenName,
            familyName: userRepresenting.name.familyName)
        self.email = userRepresenting.email
    }
}

现在您可以将任何 UserRepresenting 转换为此 AnyUserRepresenting,并改为使用 AnyUserRepresenting