Определение нескольких типов

#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