Есть ли способ использовать троичный оператор - или аналогичный метод - для выбора переменной для присвоения? - PullRequest
0 голосов
/ 18 ноября 2018

возможно ли отличить переменную, которую я назначаю, в зависимости от условия?Проблема, с которой я столкнулся, хочет сделать это:

(bEquipAsSecondary ? currentWeaponOffhand : currentWeaponMainhand) = weaponToSwitchTo;

Вместо

if (bEquipAsSecondary)
{
    currentWeaponOffhand = weaponToSwitchTo;
}
else
{
    currentWeaponMainhand = weaponToSwitchTo;
}

Что приводит к следующей ошибке

Ошибка CS0131 Слевасторона присвоения должна быть переменной, свойством или индексатором

Так что мне было интересно, есть ли способ сделать это, чтобы сократить используемое пространство и - на мой взгляд - сделать так, чтобы оно выгляделонемного аккуратнее?

Ответы [ 4 ]

0 голосов
/ 19 ноября 2018

Вы можете использовать троичный оператор, чтобы выбрать действие , которое обновит правильный слот.

//Define action to equip appropriate slot
var equipAction = bEquipAsSecondary
    ? new Action<Weapon>( w => currentWeaponOffhand = w )
    : new Action<Weapon>( w => currentWeaponMainhand = w );

//Now equip the weapon
equipAction(weaponToSwitchTo);
0 голосов
/ 18 ноября 2018

Не уверен, что это действительно эффективно, но если вы действительно хотите придерживаться троичного оператора, вот мои 2 цента ...

(bEquipAsSecondary ? ref currentWeaponOffhand : ref currentWeaponMainhand) = weaponToSwitchTo;
0 голосов
/ 18 ноября 2018

Лучшее / самое близкое к тому, что вы после этого я мог придумать, это:

_ = condition ? (a = c) : (b = c);

В контексте:

bool condition = true;
int a = 1;
int b = 2;
int c = 3;

_ = condition ? (a = c) : (b = c);
Console.WriteLine($"a = {a}; b = {b}; c = {c}");

Выходы

a = 3; b = 2; c = 3

Объяснение

  • _ = требуется, поскольку троичный оператор доступен, только если мы назначаем что-то. Здесь мы используем _ в качестве переменной discard; то есть нас не волнует возвращаемое значение; только то, что сама операция происходит.

  • использование скобок (например, (a = c) требуется вокруг назначения, так как нам нужно вернуть результат. Выполнение a = c присваивает значение c a, но ничего не возвращает вызывающий. (a = c), с другой стороны, присваивает c a, а затем выводит новое значение a, делая его доступным в контексте остальной части метода, т. е. Aisde из присваиваний этого оператора эффективно читает _ = condition ? c : c;.

    • Все остальное как и следовало ожидать от стандартного троичного оператора. Тем не менее, любые вопросы, пожалуйста, скажите.

Мысли

Это не идеально, так как вы должны указывать каждое назначение отдельно; но дает вам стенографию ...

Я подозреваю, что в обзоре кода это, как правило, не одобряется, поскольку он менее читабелен, чем стандартный подход if / else ...


NB. В некоторых других языках можно использовать хитрость, заключающуюся в том, чтобы условие выступало в качестве индекса для массива (например, false = 0, true = 1), а затем использовало его при присвоении значения ... Однако, хотя вы можете заставить это сделать в C #, это не красиво:

void Main()
{
    bool condition = true;  
    var a = new ReferenceType<int>(1);
    var b = new ReferenceType<int>(2);
    var c = new ReferenceType<int>(3);

    (new []{b, a})[ConvertBoolToInt(condition)].Value = c.Value;
    Console.WriteLine($"a = {a}; b = {b}; c = {c}");    
}
//in C# bools aren't natively convertable to int as they are in many langauges, so we'd need to provide a conversion method
public int ConvertBoolToInt(bool value)
{
    return value ? 1 : 0;
}
//to allow us to change the value rather than only the refenence, we'd need to wrap our data in something and update the value assigned to its property
public class ReferenceType<T>
{
    public T Value {get;set;}
    public ReferenceType(T intValue)
    {
        Value = intValue;
    }
    public override string ToString()
    {
        return Value.ToString();
    }
}

Тем не менее, вышеизложенное говорит о создании более общего подхода ... Если ваш вариант использования ограничен назначением оружия / аналогичными сценариями использования, вы можете использовать аналогичный прием, такой как этот:

public enum WeaponHandEnum
{
    Primary //int = 0
    ,
    Secondary //int = 1
}
public class Player
{
    public Weapon[] Weapons {get;set;}
    public Player(Weapon primary = null, Weapon secondary = null)
    {
        Weapons = new Weapon[2] {primary, secondary}; 
    }
    public void Equip(WeaponHandEnum whichHand, Weapon newWeapon)
    {
        Weapons[(int)whichHand] = newWeapon;
    }
}
public class Weapon{ /* ... */ }

Если вы обнаружите, что вам приходится делать много таких утверждений в коде, и вы хотите, чтобы ваш счетчик строк находился в обратном порядке, вам лучше всего что-то вроде этого:

void Main()
{
    bool condition = true;  
    var a = 1;
    var b = 2;
    var c = 3;
    SomeKindaHelper.TernaryAssign<int>(condition, ref a, ref b, c);
    Console.WriteLine($"a = {a}; b = {b}; c = {c}");    
}
public static class SomeKindaHelper
{
    public static void TernaryAssign<T>(bool condition, ref T assignToMeIfConditionIsTrue, ref T assignToMeIfConditionIsFalse, T newValue)
    {
        if (condition)
        {
            assignToMeIfConditionIsTrue = newValue;
        }
        else 
        {
            assignToMeIfConditionIsFalse = newValue;
        }
    }
}

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

0 голосов
/ 18 ноября 2018

Не уверен, что троичный оператор является лучшим выбором, чем обычный оператор if-else здесь. Но вы можете использовать Action, примерно так:

(bEquipAsSecondary ? new Action(() => currentWeaponOffhand = weaponToSwitchTo)
                   : () => currentWeaponMainhand = weaponToSwitchTo)();
...