#swift #generics #protocols
#swift #обобщения #протоколы
Вопрос:
Экспериментируя с дженериками в Swift, я столкнулся с этой проблемой и не могу найти ответ.
Допустим, у меня есть следующий код:
protocol Component {
}
protocol Contains {
associatedtype CompType: Component
var components: [CompType] { get set }
}
Здесь типы, которые реализуют, Contains
должны иметь возможность хранить любой тип, который реализует Component
протокол, указанный их типами. Если я расширю код до следующего, он будет работать так, как ожидалось.
protocol Component {
}
struct SomeComponent: Component {
init() {
}
}
struct AnotherComponent: Component {
init() {
}
}
protocol Contains {
associatedtype CompType: Component
var components: [CompType] { get set }
}
struct Container: Contains {
typealias CompType = SomeComponent
var components: [SomeComponent]
}
var x = Container(components: [SomeComponent()]) // works perfectly!
var y = Container(components: [AnotherComponent()]) // fails as expected
Наконец, вопрос: возможно ли заставить Container.components
принимать оба SomeComponent
и AnotherComponent
, но отклонять другие типы, которые реализуют Component
протокол? Другими словами, может typealias
содержать более одного типа?
Спасибо!
Комментарии:
1. Создайте другой протокол, который только
SomeComponent
иAnotherComponent
наследуется от2. Короткий ответ, нет. Псевдоним типа служит для разрешения общего связанного типа. Он должен однозначно преобразовать его в один тип; это и есть разрешение. Судя по тому, как вы все настроили, этот тип может и должен быть любым использующим компонентом. Если это не то, что вы хотели, вам не следовало настраивать все таким образом.
3. @matt совершенно ясный ответ, который я искал, спасибо!
4. Что ж, я попробую дать это в качестве ответа…
Ответ №1:
Вы можете соответствовать нескольким протоколам
typealias CompType = SomeComponent amp; AnotherComponent
Или вы можете изменить логику
protocol Component {
}
struct SomeComponent: Component {
init() {
}
}
struct AnotherComponent: Component {
init() {
}
}
protocol Contains {
var components: [Component] { get set }
}
struct Container: Contains {
var components: [Component]
}
var x = Container(components: [SomeComponent()])
var y = Container(components: [AnotherComponent()])
Комментарии:
1. Работает ли это для вас? Я получаю
Non-protocol, non-class type 'AnotherComponent' cannot be used within a protocol-constrained type
. В моем примереSomeComponent
иAnotherComponent
были структурами, а не протоколами. Однако при изменении его на протоколы компилятор сказал, что этоContainer
больше не соответствуетContains
протоколу.2. Я проверяю, один момент
Ответ №2:
Псевдоним типа служит для разрешения общего связанного типа. Он должен однозначно преобразовать его в один тип; это и есть разрешение.
Что ж, судя по тому, как вы все настроили, этот тип может и должен быть любым использующим компонент. Вот что означает эта строка:
associatedtype CompType: Component
Вы использовали общее ограничение компонента. Если это не то, что вы хотели, вам не следовало настраивать все таким образом. Как предлагали другие, если вы хотите, чтобы только SomeComponent и Another component удовлетворяли общему ограничению, тогда вам нужно было бы использовать протокол, который принимают только SomeComponent и Another Component.
Комментарии:
1. это все равно приведет к сбою, когда в структуре он определяет
typealias
в конформере2. @dmlebron Но в этом весь смысл моего ответа. Containes — это универсальный протокол. Один из них содержит adopter, его контейнер, может разрешить общий только одним способом. «Один тип, однозначно» — это то, что я сказал. Похоже, у него какое-то неправильное представление о том, что делает typealias, и я пытаюсь это исправить.
Ответ №3:
Я не думаю, что вам вообще нужен associatedtype
protocol Component {
}
struct SomeComponent: Component {
init() {
}
}
struct AnotherComponent: Component {
init() {
}
}
protocol Contains {
var components: [Component] { get set }
}
struct Container: Contains {
var components: [Component]
}
var x = Container(components: [SomeComponent()])
var y = Container(components: [AnotherComponent()])
Используйте associatedtype
в протоколах, когда вам нужно указать тип.
В этом случае вы не хотите указывать, потому что вы хотите иметь возможность принимать соответствие протоколу
Комментарии:
1. Это верно, за исключением того факта, что я не хочу принимать все типы, которые реализуют Component, только SomeComponent и AnotherComponent.
2. тогда
SomeComponent
иAnotherComponent
должны быть протоколы, соответствующиеComponent