Постфиксный префикс 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
как обычные целые числа.