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