【问题标题】:Swift: Make two types with the same "shape" conform to a common protocolSwift:使具有相同“形状”的两种类型符合通用协议
【发布时间】:2021-02-16 08:16:19
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: swift swift-protocols


    【解决方案1】:

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

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

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

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

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

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

    如果您的某个类/结构需要存储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
        }
    }
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-07-24
      • 1970-01-01
      • 2015-01-21
      • 1970-01-01
      • 2014-12-09
      • 2019-11-23
      • 1970-01-01
      相关资源
      最近更新 更多