2009-03-21 12 views
15

Tengo problemas para intentar sobrecargar el operador de incremento de puesto en C#. Usando enteros obtenemos los siguientes resultados.Sobrecarga del operador posterior al incremento

int n; 

n = 10; 
Console.WriteLine(n); // 10 
Console.WriteLine(n++); // 10 
Console.WriteLine(n); // 11 

n = 10; 
Console.WriteLine(n); // 10 
Console.WriteLine(++n); // 11 
Console.WriteLine(n); // 11 

Pero, cuando intento usar clases, parece que los objetos se intercambian.

class Account 
{ 
    public int Balance { get; set; } 
    public string Name { get; set; } 

    public Account(string name, int balance) 
    { 
     Balance = balance; 
     Name = name; 
    } 

    public override string ToString() 
    { 
     return Name + " " + Balance.ToString(); 
    } 

    public static Account operator ++(Account a) 
    { 
     Account b = new Account("operator ++", a.Balance); 
     a.Balance += 1; 
     return b; 
    } 

    public static void Main() 
    { 
     Account a = new Account("original", 10); 

     Console.WriteLine(a); // "original 10" 

     Account b = a++; 

     Console.WriteLine(b); // "original 11", expected "operator ++ 10" 
     Console.WriteLine(a); // "operator ++ 10", expected "original 11" 
    } 
} 

Depuración de la aplicación, el método operador sobrecargado, devuelve el nuevo objeto con el valor antiguo (10) y el objeto que se ha pasado por referencia, tiene el nuevo valor (11), pero finalmente los objetos se intercambiado. ¿Por qué está pasando esto?

+0

No debe modificar la pregunta después de la respuesta , es confuso para las próximas personas que lo miran. – RedGlyph

+0

posible duplicado de [¿Cuál es la diferencia entre X = X ++; vs X ++ ;?] (http://stackoverflow.com/questions/226002/whats-the-difference-between-xx-vs-x) – nawfal

Respuesta

11

La clave está en entender cómo funciona la línea Account b = a++;. Teniendo en cuenta cómo su código está escrito, esta línea es el equivalente a esto:.

Account b = a; 
a++; 

Y ese es el orden en que se ejecutará en la asignación efectiva (1) ocurre antes del incremento. Entonces, el primer efecto de esta línea es que a y b ambos se refieren al objeto original a.

Ahora se evaluará la parte ++. Dentro del método del operador, incrementamos el Balance del objeto original. En este punto a y b ambos apuntan al original, con Balance de 11, y b continuarán haciéndolo.

Sin embargo, ha creado un nuevo objeto dentro del método del operador y lo ha devuelto como resultado del operador. a ahora se actualizará para apuntar al objeto recién creado.

Así, un ahora apunta a un nuevo objeto, mientras que b sigue apuntando a la original. Es por eso que la salida WriteLine aparece intercambiada.

Como @MarkusQ señaló, el operador ++ se pretende hacer modificaciones en el lugar. Al generar un nuevo objeto, estás rompiendo esa suposición. La sobrecarga del operador en los objetos es un tema complicado, y este es un excelente ejemplo de por qué es mejor evitarlo en la mayoría de los casos.


1 - Sólo por el bien de la precisión, la asignación no ocurran antes del incremento cuando se trata de los operadores en los objetos, pero el resultado final es el mismo en este caso. En realidad, se copia la referencia del objeto original, la operación se lleva a cabo en el original y luego la referencia copiada se asigna a la variable de la izquierda. Es más fácil explicar si pretendes que la asignación suceda primero.

Lo que realmente está sucediendo es que este:

Account b = a++; 

resultados en esta, debido a la forma en que el operador ++ funciona en objetos:

Account copy = a; 

Account x = new Account("operator ++", a.Balance); 
a.Balance += 1; // original object's Balance is incremented 
a = x; // a now points to the new object, copy still points to the original 

Account b = copy; // b and copy now point at the same, original, object 
+0

Buena explicación (no estoy seguro si es correcto, pero tiene sentido :-). No, en serio, esto es bueno. +1. – paxdiablo

+0

+1 por pedantería. – Brian

+0

Creo que es ** bueno ** crear un nuevo objeto dentro del cuerpo del método del operador. Solo cambie el nuevo objeto y no mute el objeto original (llamado 'a') que se le ha asignado. De esa manera, funcionará como una estructura. El compilador C# hará la asignación correctamente entonces, ya sea pre o post. –

13

Mi primer pensamiento fue señalar que la semántica normal de ++ es modificación en el lugar. Si desea imitar que escribiría:

public static Account operator ++(Account a) 
{ 
    a.Balance += 1; 
    return a; 
} 

y no crea un objeto nuevo.

Pero luego me di cuenta de que intentabas imitar el incremento de la publicación.

Así que mi segundo pensamiento es "no hagas eso" - la semántica no se correlaciona bien con los objetos, ya que el valor que se "usa" es realmente un lugar de almacenamiento mutable. Pero a nadie le gusta que un extraño al azar le diga "no hagas eso", así que dejaré Microsoft tell you not to do it. Y me temo que su palabra es definitiva en tales asuntos.

P.S. En cuanto a por qué está haciendo lo que hace, realmente está anulando el operador preincremento, y luego usándolo como si fuera el operador postincremento.

+1

página no está disponible públicamente, y no existe :-) Sólo mi suerte. ¿Recuerdas el título o qué buscar? – greenoldman

1

Siempre debe devolver el valor modificado. C# usa esto como el nuevo valor y devuelve el valor antiguo o nuevo según corresponda para el operador.