Как реорганизовать этот типозависимый код, чтобы избежать понижающего приведения?C#

Место общения программистов C#
Ответить Пред. темаСлед. тема
Anonymous
 Как реорганизовать этот типозависимый код, чтобы избежать понижающего приведения?

Сообщение Anonymous »

Общая информация
Я пишу код в Unity на C#8.0.
Я создаю игру, в которой игрок сражается с врагами, играя в карты. которые имеют разные эффекты (например, Slay the Spire). Игрок выбирает карту, затем выбирает цель карты, а затем (если цель действительна) вызывается эффект карты.
Целью карты может быть множество различных игровых объектов. : враги, персонаж игрока, другие карты, счетчик хода, небо и т. д. Для каждого объекта, который может быть целью, у меня есть класс, реализующий интерфейс:
public interface ICardTarget
{
public bool IsValidTarget(Card targetingCard);
}

Аналогично, множество различных игровых объектов могут играть в карты: персонаж игрока, враги, объекты препятствий и т. д. Для каждого объекта, который может использовать карту, у меня есть класс, реализующий интерфейс:< /p>
public interface ICardUser
{
//I don't have anything in here, this is really more of just a tag to group together any card users. Am I using interfaces right?
}

Итак, некоторые объекты в моей сцене могут быть:
public class Enemy : ICardTarget, ICardUser
{
public bool IsValidTarget(Card targetingCard) {/*...*/}

public string Species {get;set;}
}

public class PlayerCharacter : ICardTarget, ICardUser
{
public bool IsValidTarget(Card targetingCard) {/*...*/}

public float AttackSpeed {get;set;}
public int Health {get;set;}
}

public class Weather : ICardTarget
{
public bool IsValidTarget(Card targetingCard) {/*...*/}

public bool IsCloudy {get;set;}
}

public class Boulder: ICardUser
{
public int Mass {get;set;}
}

и т. д.
Каждая карточка определяет, какие типы целей действительны. Когда игрок (или любой пользователь карты, если уж на то пошло) выбирает объект в качестве цели карты, карта передает себя функции IsValidTarget и получает логический ответ о том, является ли это допустимой целью для этой карты.public class Card
{
private ICardUser _user; //reference to this card's user, set somewhere else
private ICardTarget _target; //reference to this card's target, set somewhere else

public void CheckTarget()
{
if (_target.IsValidTarget(this))
{
PerformCardEffect();
}
}

private void PerformCardEffect() {/*whatever the card does*/}
}

Пока все хорошо! Теперь карта должна оказать воздействие на целевой объект, а также может оказать влияние на пользователя карты.
Проблема
В настоящее время единственное, что карта знает об этом целевом объекте, это то, что это действительная цель (в конце концов, это все, что гарантирует интерфейс ICardTarget). Так как же мне выполнить эффект карты, который зависит от типа цели, без проверки типа и понижения?
Точно такая же проблема применима и к пользователю карты: все, что я знаю, это то, что Карта пришла от пользователя карты, как я могу взаимодействовать с ней без понижения?
Или понижение — это не тот бугимен, о котором я думаю?
Попытка решения 1Я не беспокоюсь об этом и привожу цель/пользователя к одному из классов, который его реализует, когда запускается PerformCardEffect():
public class Card
{
private ICardUser _user;
private ITarget _target;

public PerformCardEffect()
{
if (_target is Weather w)
{
w.IsCloudy = true;
}
if (_target is Enemy e)
{
print(e.Species);
}
if (_target is PlayerCharacter p)
{
p.AttackSpeed *= 2;
}

if (_user is PlayerCharacter p)
{
p.Health -= 3;
}
if (_user is Enemy e)
{
e.Health -= 5;
}

//etc.
}
}

Мне сейчас легче всего это повторить, но я знаю, что это не одобряется.
Попытка решения 2
Я модифицирую ICardTarget/ICardUser так, чтобы ответственность за вызов правильной функции была перенесена с Card на них:
public interface ICardTarget
{
//...
public void SelectCardEffectTarget(Card targetingCard);
//...
}

public interface ICardUser
{
public void SelectCardEffectUser(Card targetingCard);
}

public class Enemy : ICardTarget, ICardUser
{
//...
public void SelectCardEffectTarget(Card targetingCard)
{
targetingCard.PerformCardEffectTarget(this);
}

public void SelectCardEffectUser(Card targetingCard)
{
targetingCard.PerformCardEffectUser(this);
}
//...
}

public class PlayerCharacter : ICardTarget, ICardUser
{
//...
public void SelectCardEffectTarget(Card targetingCard)
{
targetingCard.PerformCardEffectTarget(this);
}

public void SelectCardEffectUser(Card targetingCard)
{
targetingCard.PerformCardEffectUser(this);
}
//...
}

public class Weather : ICardTarget
{
//...
public void SelectCardEffectTarget(Card targetingCard)
{
targetingCard.PerformCardEffectTarget(this);
}
//...
}

public class Card
{
private ICardUser _user;
private ITarget _target;

public void CheckTarget()
{
if (_target.IsValidTarget(this))
{
_target.SelectCardEffectTarget(this);
_user.SelectCardEffectUser(this);
}
}

public void PerformCardEffectTarget(Weather w)
{
w.IsCloudy = true;
}
public void PerformCardEffectTarget(Enemy e)
{
print(e.Species);
}
public void PerformCardEffectTarget(PlayerCharacter p)
{
p.AttackSpeed *= 2;
}

public void PerformCardEffectUser(Enemy e)
{
e.Health -= 5;
}
public void PerformCardEffectUser(PlayerCharacter p)
{
p.Health -= 3;
}

//I have to include these calls so that any objects not caught by the above overloaded parameters default to doing nothing.
public void PerformCardEffectTarget(ICardTarget t) {}
public void PerformCardEffectUser(ICardUser u) {}
}

Это кажется лучше, но здесь много ссылок, которые передаются туда и обратно, что неплохо, но мне кажется это немного чрезмерным. Мне также нужно раскрыть множество методов как на Card, так и на интерфейсах.
P.S. Есть ли более короткий способ написания SelectCardEffectTarget()/SelectCardEffectUser(), чтобы мне не приходилось повторять один и тот же блок кода в каждом подклассе? Я знаю, что мог бы превратить ICardTarget/ICardUser в класс и установить там функцию, чтобы она наследовалась, но это не кажется подходящей иерархией для данной ситуации.
Попытка Решение 3 (не удалось)
Я пытался использовать общий класс для указания типов целей/пользователей, но не смог найти способ задействовать его и получить от него полезную функциональность.
public class CardEffectDetails where T : ICardTarget where U : ICardUser
{
T Target {get;set;}
U User {get;set;}
}

public class Card
{
private ICardTarget _target;
private ICardUser _user;

private void CreateEffectDetails()
{
CardEffectDetails effectDetails = new();
//what can I even do with this thing?
}
}


Подробнее здесь: https://stackoverflow.com/questions/787 ... owncasting
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «C#»