#swift #generics #protocols #type-erasure
#swift #дженерики #протоколы #тип-стирание
Вопрос:
Я пытаюсь разработать простой игровой движок для развлечения и образовательных целей. У меня есть Game
протокол, который представляет мою игру, и Entity
протокол, который представляет сущность (например, игрока или противника). Наконец, у меня есть EntityComponent
протокол, реализации которого обновляют Entity
. В Swift это выглядит так:
protocol Game {
var entities: [Entity] { get }
}
protocol Entity {
var components: [EntityComponent] { get }
}
protocol EntityComponent {
func update(_ entity: Entity, deltaTime seconds: TimeInterval)
}
Я хочу, чтобы мои компоненты сущности были обобщены с использованием объекта, который они обновляют. В Swift я могу использовать associatedtype
:
protocol EntityComponent {
associatedtype EntityType: Entity
func update(_ entity: EntityType, deltaTime seconds: TimeInterval)
}
Однако это приведет к ошибке компиляции для Entity
протокола:
protocol Entity {
var components: [EntityComponent] { get } // ERROR!
}
Протокол ‘EntityComponent’ может использоваться только как общее ограничение, поскольку он имеет собственные или связанные требования к типу
Эта проблема может быть решена путем определения стирания типа для EntityComponent
протокола и обновления Entity
следующим образом:
protocol Entity {
var components: [AnyEntityComponent<Self>] { get }
}
final class AnyEntityComponent<EntityType: Entity>: EntityComponent {
init<T: EntityComponent>(_ component: T) where T.EntityType == EntityType {
self.update = component.update
}
func update(_ entity: EntityType, deltaTime seconds: TimeInterval) {
update(entity, seconds)
}
private let update: (EntityType, TimeInterval) -> Void
}
К сожалению, это изменение в Entity
протоколе создает еще одну проблему. На этот раз в Game
протоколе:
protocol Game {
var entities: [Entity] { get } // ERROR!
}
«Сущность» протокола может использоваться только как общее ограничение, поскольку у него есть требования к собственному или связанному типу
Я не уверен, как это исправить, поскольку я не могу этого сделать, определив стирание типа (как в случае EntityComponent
).
Я буду признателен за любые подсказки и идеи!
Ответ №1:
Это может помочь удалить связанные требования к типу и вместо этого использовать дженерики:
protocol EntityComponent {
func update<T: Entity>(_ entity: T, deltaTime seconds: TimeInterval)
}
Комментарии:
1. Это хороший намек, спасибо! Тем не менее, я хотел бы, чтобы одна
EntityComponent
реализация обновляла однуEntity
реализацию, напримерclass PlayerEntity
, иclass PlayerEntityComponent
.2. Ах, это имеет смысл. Недавно у меня было такое же требование, и я не мог найти элегантный способ сделать это.
3. Проверьте: hackingwithswift.com/articles/74 /. … У меня это не сработало, потому что я использовал структуры, а не классы, но я думаю, что это поможет вам.
4. Большое спасибо, я проверю это