Быстрое удаление типа для протокола с требованием самостоятельного ввода

#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. Большое спасибо, я проверю это