首页 > 解决方案 > Swift:使具有相同“形状”的两种类型符合通用协议

问题描述

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

假设这是我的两种代码生成类型,我一直坚持:

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 {}

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

标签: swiftswift-protocols

解决方案


生成的结构的name属性具有 type Name,而不是NameRepresenting协议要求的。Swift 尚不支持协变返回 :(

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

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

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

但是,既然它具有关联的类型要求,则不能UserRepresenting用作变量/函数参数的类型。您只能在通用约束中使用它。因此,如果您有一个需要 a 的函数,则UserRepresenting需要这样编写:

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

}

如果您的某个类/结构需要存储 type 的属性UserRepresenting,则您也需要使您的类/结构成为通用的:

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

现在您可以将 any 转换UserRepresenting为 this AnyUserRepresenting,然后使用AnyUserRepresenting


推荐阅读