#swift #swift-protocols
#быстрый #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 {}
Я ожидаю, что вышеизложенное сработает, но компиляция завершается с ошибкой, описанной выше. Есть ли какой-нибудь элегантный способ привести эти два типа в соответствие с общим протоколом, чтобы я мог использовать их взаимозаменяемо?
Ответ №1:
name
Свойства сгенерированных структур имеют тип Name
, а не NameRepresenting
как того требует протокол. Ковариантные возвраты пока не поддерживаются в Swift: (
Что вы можете сделать, так это добавить соответствующее требование к типу:
protocol UserRepresenting {
associatedtype Name : NameRepresenting
var email: String { get }
var name: Name { get }
}
Для этого требуется, чтобы конформеры имели тип, который соответствует NameRepresenting
и является типом name
свойства.
Однако теперь, когда у него есть соответствующее требование к типу, вы не можете использовать UserRepresenting
его в качестве типа параметра переменной / функции. Вы можете использовать его только в общих ограничениях. Итак, если у вас есть функция, которая принимает a 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
ним вместо этого.