Постфиксный префикс C# и разница приращения приращения / приращения префикса

В большинстве источников говорится, что перегрузка операторов ++ и - в C# приводит к перегрузке как postfix, так и prefix одновременно. Но похоже, что их поведение по-прежнему отличается.

class Counter
{
    public Counter(int v = 0)
    {
        this.v = v;
    }
    public Counter(Counter c)
    {
        v = c.v;
    }
    public int GetValue() { return v; }
    public static Counter operator ++(Counter c)
    {
        c.v++;
        return new Counter(c);
    }
    private int v;
}


class Program
{
    public static void Main()
    {
        Counter c1 = new Counter(1);

        Counter c2 = c1++;

        Counter c3 = ++c1;

        c3++;

        System.Console.WriteLine("c1 = {0}", c1.GetValue());
        System.Console.WriteLine("c2 = {0}", c2.GetValue());
        System.Console.WriteLine("c3 = {0}", c3.GetValue());
    }
}

Замечательно, хотя перегружен operator ++ возвращает копию исходного класса, в этом примере c1 а также c3 становится ссылкой на один и тот же объект, в то время как c2 указывает на другой (c1=4, c2=2, c3=4 Вот). изменения Counter c3 = ++c1; в Counter c3 = c1++; выходы c1=3, c2=2, c3=4,

Итак, какова точная разница между постфиксным и префиксным приращением / уменьшением и как это влияет на перегрузку? Являются ли эти операторы одинаковыми для классов и для примитивных типов?

1 ответ

Решение

Это неправильный способ реализации увеличения и уменьшения в C#. Вы получите сумасшедшие результаты, если сделаете это неправильно; Вы сделали это неправильно, вы получили сумасшедшие результаты, поэтому система работает.:-)

По совпадению я написал статью об этой самой теме на прошлой неделе:

http://ericlippert.com/2013/09/25/bug-guys-meets-math-from-scratch/

Как указывает комментатор dtb, правильная реализация:

    public static Counter operator ++(Counter c)
    {
        return new Counter(c.v + 1);
    }

В C# оператор приращения не должен изменять свой аргумент. Скорее он должен только вычислить увеличенное значение и вернуть его, не вызывая побочных эффектов. Побочный эффект изменения переменной будет обработан компилятором.

С этой правильной реализацией ваша программа теперь выглядит так:

    Counter c1 = new Counter(1);

Вызовите объект, на который ссылается c1 прямо сейчас. W, W.v это 1.

    Counter c2 = c1++;

Это имеет семантику:

temp = c1
c1 = operator++(c1) // create object X, set X.v to 2
c2 = temp

Так c1 теперь относится к X, а также c2 относится к W, W.v это 1 и X.v это 2.

    Counter c3 = ++c1;

Это имеет семантику

temp = operator++(c1) // Create object Y, set Y.v to 3
c1 = temp
c3 = temp

Таким образом, c1 и c3 теперь оба относятся к объекту Y, а также Y.v это 3.

    c3++;

Это имеет семантику

c3 = operator++(c3) // Create object Z, set Z.v to 4

Так что, когда дым все очищает

c1.v = 3 (Y)
c2.v = 1 (W)
c3.v = 4 (Z)

а также X осиротел.

Это должно дать точно такие же результаты, как если бы вы имели c1, c2 а также c3 как обычные целые числа.

Другие вопросы по тегам