C #: Укажите, что функция arg должна наследоваться от одного класса, и реализовать интерфейс? - PullRequest
2 голосов
/ 29 марта 2010

Я создаю игру, в которой каждый Актер представлен GameObjectController. Игровые объекты, которые могут участвовать в бою, реализуют ICombatant. Как я могу указать, что аргументы боевой функции должны наследоваться от GameObjectController и реализовывать ICombatant? Или это указывает на то, что мой код плохо структурирован?

public void ComputeAttackUpdate(ICombatant attacker, AttackType attackType, ICombatant victim)

В приведенном выше коде я хочу, чтобы attacker и victim наследовали от GameObjectController и реализовали ICombatant. Возможно ли это синтаксически?

Ответы [ 5 ]

6 голосов
/ 29 марта 2010

Я бы сказал, что это, вероятно, означает, что вы можете каким-то образом реструктурировать, например, иметь базовый класс Combatant, от которого наследуются злоумышленник и жертва, который наследует от GameObjectController и реализует ICombatant.

однако, вы могли бы сделать что-то вроде

ComputeAttackUpdate<T,U>(T attacker, AttackType attackType, U victim)
      where T: ICombatant, GameObjectController
      where U: ICombatant, GameObjectController

Хотя я бы, наверное, не стал.

4 голосов
/ 29 марта 2010

Предположительно, все ICombatants также должны быть GameObjectControllers? Если это так, вы можете создать новый интерфейс IGameObjectController, а затем объявить:

interface IGameObjectController
{
    // Interface here.
}

interface ICombatant : IGameObjectController
{
    // Interface for combat stuff here.
}

class GameObjectController : IGameObjectController
{
    // Implementation here.
}

class FooActor : GameObjectController, ICombatant
{
    // Implementation for fighting here.   
}
2 голосов
/ 29 марта 2010

Синтаксически возможно только если GameObjectController сам реализует ICombatant; в противном случае я бы сказал, что у вас проблемы с дизайном.

Интерфейсы предназначены для определения операций , доступных для некоторого объекта; базовые классы идентифицируют что такое этот объект. Вы можете выбрать только один или другой. Если принятие интерфейса ICombatant в качестве аргумента недостаточно, это может означать, что ICombatant определен слишком узко (т. Е. Не поддерживает все, что вам нужно для этого).

Мне нужно увидеть специфику того, что вы пытаетесь сделать с этим объектом, чтобы углубиться в глубину.

Что если вы сделали это вместо этого:

public class GameObjectControllerCombatant : GameObjectController, ICombatant
{
    // ...
}

Затем выведите свои классы бойцов из этого, а не непосредственно из GameObjectController. Мне все еще кажется, что это нарушает инкапсуляцию, и неловкость названия является ярким свидетельством того, что ваши классы комбатантов нарушают Принцип единой ответственности ... но это сработает.

1 голос
/ 29 марта 2010

Ну, вроде как. Вы можете написать общий метод:

public void ComputeAttackUpdate<T>(T attacker, AttackType type, T victim)
    where T : GameObjectController, ICombatant

Это означает, что T должен удовлетворять обоим нужным ограничениям. Это довольно мрачно - и если злоумышленник и жертва могут быть разных (несколько не связанных) типов, вам придется вместо этого сделать его универсальным по двум параметрам типа.

Однако я лично попытался бы найти более естественное решение. Конечно, я не в такой ситуации. Если вам нужно рассматривать аргумент двумя разными способами, возможно, вам действительно нужны два разных метода?

0 голосов
/ 27 мая 2011

Если вы управляете всеми классами, о которых идет речь, и если GameObjectController не определяет какие-либо поля, самым чистым подходом будет определение IGameObjectController (свойства и методы которого соответствуют свойствам GameObjectController) и ICombatantGameObjectContoller (который является производным от обоих IGameObjectController) и ICombatant). Каждый класс, который должен использоваться в ситуациях, когда требуются оба интерфейса, должен быть явно объявлен как реализующий ICombatantGameObjectController, даже если добавление этого объявления не потребует добавления дополнительного кода. Если это так, можно без труда использовать параметры, поля и переменные типа ICombatantGameObjectController.

Если вы не можете настроить свои классы и интерфейсы, как описано выше, подход, предложенный Джоном Скитом, в целом хорош, но с неприятным предостережением: для вызова универсальной функции, такой как ComputeAttackUpdate г-на Скита, компилятор имеет уметь определять единственный тип, который, как он знает, совместим с типом передаваемого объекта и со всеми ограничениями. Если есть потомки GameObjectController, которые реализуют ICombatant, но не являются производными от общего базового типа, который также реализует GameObjectController, может быть сложно хранить такие объекты в поле, а затем передавать их в общие подпрограммы. Есть способ, и если вам нужно, я могу объяснить это, но это немного сложно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...