Абстрагирование условной доходности в c#

#c# #unity3d #coroutine #yield-return

Вопрос:

Я пишу мод для игры, написанной на C#, в которой широко используются сопрограммы Unity. Существует шаблон программирования, который требуется каждый раз, когда пишется сопрограмма для модов для этой игры, который выглядит следующим образом:

 IEnumerator e = SomeCoroutineFunc();
if (UseUnityCoroutines) { yield return GameController.StartCoroutine(e); }
else { GameController.ExhaustCoroutine(e); }
 

Я хотел бы заменить блок запуска или выпуска сопрограммы какой-нибудь абстракцией, чтобы мне не приходилось все время писать один и тот же фрагмент кода. То есть я хотел бы иметь возможность писать:

 IEnumerator e = SomeCoroutineFunc();
DoCoroutine(e);
 

(или какой — то аналогичный эквивалент).

Но поскольку фрагмент приводит только к одному пути, я не уверен, как это сделать! В C я бы использовал макрос, но я не знаю ни одного механизма перезаписи на уровне исходного кода в C#, который мог бы достичь аналогичного эффекта? Есть ли какая-то особенность языка, о которой я не знаю, которая позволила бы мне это сделать?

Более полный пример кода с некоторым контекстом:

     public class PureStrengthCardController : CardController
    {
        public PureStrengthCardController(Card card, TurnTakerController controller) : base(card, controller)
        { }

        public override IEnumerator Play()
        {
            // "{AlexandriaCharacter} deals a target 4 melee damage"
            var e = GameController.SelectTargetsAndDealDamage(
                HeroTurnTakerController,
                new DamageSource(GameController, CharacterCard),
                amount: 4,
                DamageType.Melee,
                numberOfTargets: 1,
                optional: false,
                requiredTargets: 1,
                cardSource: GetCardSource()
            );
            if (UseUnityCoroutines)
            {
                yield return GameController.StartCoroutine(e);
            }
            else
            {
                GameController.ExhaustCoroutine(e);
            }
        }
    }
 

Очевидно, что я не контролирую ни один код GameController или CardController, ни более широкую архитектуру игры; Я пишу мод.

Комментарии:

1. Можете ли вы объяснить контекст, в котором это называется? в идеале, по крайней мере, с подписью метода, окружающей предоставленный вами код.

2. Добавлен полный (простой) пример

3. Нет, вы действительно не можете этого сделать. Он всегда уступал, по крайней мере, один раз … вам всегда будет нужна, по крайней мере, какая-то структура, например if(yieldRequired) { yield return null; } , где само условие может быть извлечено в выделенный класс, но yield само это должно произойти именно так

4. Есть ли в вашем моде когда-нибудь случаи, когда вы хотите yield return GameController.StartCoroutine(e) несколько раз использовать один и тот же метод?

5. Да, это действительно обычное дело, что вам нужно делать это несколько раз.

Ответ №1:

Работает ли это для вас?

 public abstract class EnhancedCardController : CardController
{
    public EnhancedCardController(Card card, TurnTakerController controller) : base(card, controller)
        { }
        
    public IEnumerator DoCoroutine(IEnumerator coroutine)
    {
        if (UseUnityCoroutines)
        {
            yield return GameController.StartCoroutine(coroutine);
        }
        else
        {
            GameController.ExhaustCoroutine(coroutine);
        }
    }   
}
 

А затем использование на вашем примере

     public class PureStrengthCardController : EnhancedCardController
    {
        public PureStrengthCardController(Card card, TurnTakerController controller) : base(card, controller)
        { }

        public override IEnumerator Play()
        {
            // "{AlexandriaCharacter} deals a target 4 melee damage"
            var e = GameController.SelectTargetsAndDealDamage(
                HeroTurnTakerController,
                new DamageSource(GameController, CharacterCard),
                amount: 4,
                DamageType.Melee,
                numberOfTargets: 1,
                optional: false,
                requiredTargets: 1,
                cardSource: GetCardSource()
            );
            
            // yield return DoCoroutine(e); EDIT: this won't compile! sorry.
            return DoCoroutine(e);
        }
    }
 

Комментарии:

1. Это все равно yield будет по крайней мере один раз

2. @derHugo это проблема? Я вообще не знаком с Unity, поэтому непонятно, как все это вписывается в общую картину. С таким же успехом можно было бы сделать это return вместо yield return , но я не уверен, насколько «упрощенным» примером это было для начала.

3. @CallumMorrison, конечно, зависит от использования .. если вы хотите пропустить кадр только при определенном условии, то да, это будет иметь значение

4. @derHugo из вопроса, однако, он говорит, что должен дублировать те же самые 3 строки кода в куче мест (предположительно всегда внутри a CardController ), если условие всегда одно и то же, и результат всегда один и тот же, почему его нельзя абстрагировать, как я сделал?

5. Привет, я не уверен, что yield в else ветке будет иметь значение или нет. Я думал , что Unity не пропускает кадр для a yield break , он пропускает только кадр для yield return null .