Me gustaría dar más detalles sobre un punto específico que Eric Lippert hizo en su respuesta y poner el foco en una ocasión particular que nadie más ha tocado. Eric dijo:
[...] una asignación casi siempre deja atrás el valor que se acaba de asignar en un registro.
Me gustaría decir que la asignación siempre dejará atrás el valor que intentamos asignar a nuestro operando izquierdo. No solo "casi siempre". Pero no lo sé porque no he encontrado este problema comentado en la documentación. Teóricamente podría ser un procedimiento implementado muy efectivo para "dejar atrás" y no reevaluar el operando izquierdo, pero ¿es eficiente?
'Eficiente' sí para todos los ejemplos hasta ahora construidos en las respuestas de este hilo. ¿Pero eficiente en el caso de las propiedades e indexadores que usan get y set accessors? De ningún modo. Considera este código:
class Test
{
public bool MyProperty { get { return true; } set { ; } }
}
Aquí tenemos una propiedad, que ni siquiera es un contenedor para una variable privada. Siempre que se le solicite, volverá verdadero, cada vez que uno intente establecer su valor, no hará nada. Por lo tanto, siempre que se evalúe esta propiedad, él será sincero. Veamos qué pasa:
Test test = new Test();
if ((test.MyProperty = false) == true)
Console.WriteLine("Please print this text.");
else
Console.WriteLine("Unexpected!!");
Adivina lo que imprime? Se imprime Unexpected!!
. Resulta que, de hecho, se llama al descriptor de acceso, que no hace nada. Pero a partir de entonces, el get getor nunca se llama en absoluto. La asignación simplemente deja atrás el false
valor que intentamos asignar a nuestra propiedad. Y este false
valor es lo que evalúa la instrucción if.
Terminaré con un ejemplo del mundo real que me hizo investigar este problema. Hice un indexador que era un contenedor conveniente para una colección ( List<string>
) que una clase mía tenía como variable privada.
El parámetro enviado al indexador era una cadena, que debía tratarse como un valor en mi colección. El get getor simplemente devolvería verdadero o falso si ese valor existiera en la lista o no. Por lo tanto, el get getor era otra forma de usar el List<T>.Contains
método.
Si se llama al conjunto de acceso del indexador con una cadena como argumento y el operando correcto es un bool true
, agregaría ese parámetro a la lista. Pero si el mismo parámetro se envió al descriptor de acceso y el operando correcto era un bool false
, en su lugar eliminaría el elemento de la lista. Por lo tanto, el conjunto de acceso se utilizó como una alternativa conveniente para ambos List<T>.Add
y List<T>.Remove
.
Pensé que tenía una "API" ordenada y compacta que envolvía la lista con mi propia lógica implementada como puerta de enlace. Con la ayuda de un indexador solo, podría hacer muchas cosas con unas pocas pulsaciones de teclas. Por ejemplo, ¿cómo puedo intentar agregar un valor a mi lista y verificar que esté allí? Pensé que esta era la única línea de código necesaria:
if (myObject["stringValue"] = true)
; // Set operation succeeded..!
Pero como mostró mi ejemplo anterior, ni siquiera se llamó al get getor que se supone que ve si el valor realmente está en la lista. El true
valor siempre se dejó atrás destruyendo efectivamente cualquier lógica que haya implementado en mi get accessor.