2011-06-26 10 views
10

He estado buscando código de trabajo real donde se ejecuta realmente un operador false sobrecargado.¿Cuándo se ejecuta el operador falso sobrecargado y para qué sirve?

This question (What's the false operator in C# good for?) es algo similar, pero la respuesta aceptada se vincula a una url que devuelve un error 404. También miré How does operator overloading of true and false work? y algunas otras preguntas.

Lo que he encontrado en casi todas las respuestas es que false solo se ejecuta cuando usa un cortocircuito y como x && y. Esto se evalúa como T.false(x) ? x : T.&(x, y).

Ok, entonces tengo el siguiente código. El struct contiene un int y se considere cierto si el int es mayor que cero .:

public struct MyStruct { 
    private int _i; 

    public MyStruct(int i) { 
     _i = i; 
    } 

    public static bool operator true(MyStruct ms) { 
     return ms._i > 0; 
    } 

    public static bool operator false(MyStruct ms) { 
     return ms._i <= 0; 
    } 

    public override string ToString() { 
     return this._i.ToString(); 
    } 
} 

Ahora espero el siguiente programa se ejecutará y el uso de la false operador sobrecargado.

class Program { 
    private static void Main() { 
     MyStruct b1 = new MyStruct(1); // to be considered true 
     MyStruct b2 = new MyStruct(-1); // to be considered false 

     Console.WriteLine(b1 && b2); 
     Console.WriteLine(b2 && b1); 
    } 
} 

Sin embargo, ni siquiera compila. Dice que no puede aplicar el operador '& &' a los operandos del tipo 'MyStruct' y 'MyStruct'.

Sé que puedo implementar una sobrecarga del operador &. Así que hagámoslo. El &debe devolver un MyStruct, por lo que no puedo hacer que devuelva un bool.

public static MyStruct operator &(MyStruct lhs, MyStruct rhs) { 
    return new MyStruct(lhs._i & rhs._i); 
} 

Ahora el código se compila. Su salida es 1 y -1. Por lo tanto, el resultado de b1 && b2 no es el mismo que el de b2 && b1.

Si puedo depurar el código, veo que b1 && b2 primero se ejecuta el operador false en b1, que devuelve false. A continuación, realiza el operador & en b1 y b2, que se realiza en modo bit y en 1 y -1, lo que da como resultado 1. Por lo tanto, en primer lugar se comprueba si b1 es falso.

La segunda expresión, b2 && b1 primero ejecuta el operador false en b2, que devuelve true. Combinado con el hecho de que estoy usando un cortocircuito, no hace nada con b1 y solo imprime el valor de b2.

Así que sí, el operador false se ejecuta cuando utiliza un cortocircuito. Sin embargo, no ejecuta el operador true o false en el segundo argumento, sino que ejecuta el operador sobrecargado & en los operandos.

¿Cuándo puede ser útil? ¿O cómo puedo hacer mi tipo para que pueda verificar si las dos variables son verdaderas?

+0

¿Ha intentado sobrecargar el operador '&&'? – Snowbear

+0

@sSnowbear: ¿qué quieres decir? No puedes sobrecargar el && o || operadores. Ver [http://msdn.microsoft.com/en-us/library/8edha89s(v=VS.100).aspx](MSDN). – comecme

+0

@Snowbear: esa era exactamente la funcionalidad que el autor del artículo original intentaba lograr, aunque Microsoft no permite sobrecargar el && y || operadores. – IAmTimCorey

Respuesta

4

EDIT-

Leyendo el artículo enlazado yo era capaz de obtener la siguiente salida que utiliza tanto los verdaderos y falsos operadores:

op false on 1 
op & on 1 -1 
op true on 1 
op true on -1 
FALSE 
op false on -1 
op true on -1 
FALSE 
op true on 1 
op true on 1 
TRUE 
op true on -1 
op & on -1 1 
op true on -1 
op true on 1 
TRUE 

con el código:

class Program 
{ 
    static void Main(string[] args) 
    { 
     MyStruct b1 = new MyStruct(1); // to be considered true 
     MyStruct b2 = new MyStruct(-1); // to be considered false 

     Console.WriteLine((b1 && b2) ? "TRUE" : "FALSE"); 
     Console.WriteLine((b2 && b1) ? "TRUE" : "FALSE"); 

     Console.WriteLine((b1 || b2) ? "TRUE" : "FALSE"); 
     Console.WriteLine((b2 || b1) ? "TRUE" : "FALSE"); 

     Console.ReadLine(); 
    } 
} 

public struct MyStruct 
{ 
    private int _i; 

    public MyStruct(int i) 
    { 
     _i = i; 
    } 

    public static bool operator true(MyStruct ms) 
    { 
     Console.WriteLine("op true on {0}", ms); 
     return ms._i > 0; 
    } 

    public static bool operator false(MyStruct ms) 
    { 
     Console.WriteLine("op false on {0}", ms); 
     return ms._i <= 0; 
    } 

    public static MyStruct operator &(MyStruct lhs, MyStruct rhs) 
    { 
     Console.WriteLine("op & on {0} {1}", lhs, rhs); 

     if (lhs) 
     { 
      return rhs; 
     } 
     else 
     { 
      return new MyStruct(-1); //-1 is false 
     } 
    } 

    public static MyStruct operator |(MyStruct lhs, MyStruct rhs) 
    { 
     Console.WriteLine("op & on {0} {1}", lhs, rhs); 

     if (lhs) 
     { 
      return lhs; 
     } 
     else 
     { 
      return rhs; 
     } 
    } 

    public override string ToString() 
    { 
     return this._i.ToString(); 
    } 
} 

No estoy seguro de lo que quiere decir cuando dice que el primer código no se puede compilar, aunque no utiliza el operador verdadero/falso, ejecuté lo siguiente Código en 2010 expresa y consiguió la salida:

op bool on 1 
op bool on -1 
False 
op bool on -1 
False 
op bool on -1 
op bool on 1 
True 
op bool on 1 
True 

Código:

class Program 
{ 
    static void Main(string[] args) 
    { 
     MyStruct b1 = new MyStruct(1); // to be considered true 
     MyStruct b2 = new MyStruct(-1); // to be considered false 

     Console.WriteLine(b1 && b2); 
     Console.WriteLine(b2 && b1); 

     Console.WriteLine(b2 || b1); 
     Console.WriteLine(b1 || b2); 

     Console.ReadLine(); 
    } 
} 

public struct MyStruct 
{ 
    private int _i; 

    public MyStruct(int i) 
    { 
     _i = i; 
    } 

    public static bool operator true(MyStruct ms) 
    { 
     Console.WriteLine("op true on {0}", ms); 
     return ms._i > 0; 
    } 

    public static bool operator false(MyStruct ms) 
    { 
     Console.WriteLine("op false on {0}", ms); 
     return ms._i <= 0; 
    } 

    public static implicit operator bool(MyStruct ms) 
    { 
     Console.WriteLine("op bool on {0}", ms); 
     return ms._i > 0; 
    } 

    public override string ToString() 
    { 
     return this._i.ToString(); 
    } 
} 
+0

Lo siento, no agregué el operador 'bool' hasta más tarde. Con 'bool' en él, sí compila. Revisaré mi código. – comecme

+0

Con el operador 'bool' en él, y el operador' & 'no está en él, los operadores' true' y 'false' nunca reciben llamadas. Solo 'bool' se llama. Entonces, supongo que puedo hacer que '&&' haga lo que quiero usando el operador 'bool'. Pero la pregunta sigue siendo cuando sobrecargar 'true' y' false' realmente será útil. – comecme

+0

Edité la respuesta para mostrarla usando los operadores verdadero y falso, todavía no estoy seguro de por qué querría usar este enfoque frente a la sobrecarga de bool, que sería mucho más legible. – BrandonAGr

4

El contenido de la URL que usted ha mencionado que era 404 se puede encontrar aquí:

http://web.archive.org/web/20080613013350/http://www.ayende.com/Blog/archive/2006/08/04/7381.aspx

El El artículo al que se refiere el autor está aquí:

http://web.archive.org/web/20081120013852/http://steve.emxsoftware.com/NET/Overloading+the++and++operators

para evitar el mismo problema de nuevo, aquí es lo más destacado del artículo: Hace

un par de meses he publicado sobre nuestra API de consulta junto con una explicación de cómo funciona. Nuestro API de consulta nos permite expresar nuestras consultas utilizando inflexible sintaxis de C#:

List<Customer> customers = repository.FindAll(Customer.Columns.Age == 20 & Customer.Columns.Name == “foo”); 

Una de las cosas que he señalado en mis mensajes anteriores era que no podría sobrecargar el & & y || operadores directamente ya que el marco no permite esa locura ... al menos no directamente.

En particular, no es posible sobrecargar acceso miembros, invocación de métodos, o las =, & &, ||,:?, Comprobado, sin control, nueva, typeof, como, y es operadores. http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csspec/html/vclrfcsharpspec_7_2_2.asp

En el último mes he hecho un poco de investigación sobre el tema para ver si y cómo puedo conseguir & & y || comportarse de la manera que quiero Esta tarde me encontré con la página de operadores lógicos condicionales en MSDN que me dio la respuesta que estaba buscando:

La operación x & & y se evalúa como T.false (x)? x: T. & (x, y), donde T.false (x) es una invocación del operador falso declarado en T, y T. & (x, y) es una invocación del operador seleccionado &. En otras palabras, x se evalúa primero y el operador falso se invoca en el resultado para determinar si x es definitivamente falso. Entonces, si x es definitivamente falso, el resultado de la operación es el valor previamente calculado para x. De lo contrario, se evalúa y y se invoca el operador seleccionado & en el valor calculado previamente para xy el valor calculado para y para producir el resultado de la operación. La operación x || y se evalúa como T.true (x)? x: T. | (x, y), donde T.true (x) es una invocación del operador verdadero declarado en T, y T. | (x, y) es una invocación del operador seleccionado |. En otras palabras, x se evalúa primero y el operador true se invoca en el resultado para determinar si x es definitivamente cierto. Entonces, si x es definitivamente cierto, el resultado de la operación es el valor previamente calculado para x. De lo contrario, se evalúa y, y el operador seleccionado | se invoca en el valor calculado previamente para xy el valor calculado para y para producir el resultado de la operación. Como ya tenemos el & y | operadores en el lugar era simplemente una cuestión de sobrecargar a los operadores verdadero y falso para que ambos devuelvan falso. Esto da como resultado el & y | los operadores siempre son llamados, lo que a su vez hace que los dos objetos de criterio se conviertan en un AndCriteria/OrCriteria!

Así que ahora podemos expresar nuestras expresiones de criterios usando & & y || sintaxis a la que estamos acostumbrados.

repository.FindAll(Customer.Columns.Age == 20 && Customer.Columns.Name == “foo”); 

repository.FindAll(Customer.Columns.FirstName == “Foo” || Customer.Columns.LastName == “Bar”); 

Las sobrecargas de operadores relevantes se muestran a continuación.

public static bool operator true(Criteria<T> criteria) { 
    return false; 
} 

public static bool operator false(Criteria<T> criteria) { 
    return false; 
} 

public static Criteria<T> operator &(Criteria<T> lhs, Criteria<T> rhs) { 
    return new AndCriteria<T>(lhs, rhs); 
} 

public static Criteria<T> operator |(Criteria<T> lhs, Criteria<T> rhs) { 
    return new OrCriteria<T>(lhs, rhs); 
} 
+0

Gracias por los enlaces de trabajo. Como resultado, el artículo realmente no responde mi pregunta. O si lo hace, no lo obtendré. – comecme

1

De Microsoft (http://msdn.microsoft.com/en-us/library/6292hy1k.aspx):

Antes de C# 2.0, se utilizaron los operadores de verdaderos y falsos para crear definidos por el usuario tipos de valor anulables que eran compatibles con tipos como SqlBool. Sin embargo, el idioma ahora ofrece soporte integrado para los tipos de valor anulable, y siempre que sea posible se debe utilizar éstos en lugar de sobrecargar el verdadero y falsas operadores.

Si está buscando simplemente evaluar su objeto como un booleano, elimine las sobrecargas falsas true real y operator del operador y simplemente use la sobrecarga bool.

+0

No veo cuál sería el error en la lógica de mis sobrecargas verdaderas y falsas. 'false' devuelve true si' _i <= 0' porque un valor menor o igual a cero se considera un valor falso. – comecme

+0

Tienes razón en que puedo eliminar los operadores 'true' y' false' e ​​implementar un molde implícito en 'bool' en lugar. Pero eso deja mi otra pregunta sin respuesta: ¿Cuándo puede ser útil? – comecme

+0

** No veo lo que estaría mal con la lógica ** tienes razón, mi mal. ** ¿Cuándo puede ser útil esto? ** Bueno, depende de lo que estás tratando de hacer. Si solo quieres evaluar tu objeto como booleano, entonces la sobrecarga de bool es todo lo que necesitas. son obsoletos por el sonido de la misma, al menos para su propósito principal previsto (crear tipos que aceptan nulos). Como otros han publicado, también se pueden usar para hacer una notación abreviada para LINQ y AND lógico. –

1

Respondiendo a su última pregunta: "¿cómo puedo hacer mi tipo para que pueda verificar si las dos variables son verdaderas?" - solo use el operador &. El punto completo de && es el cortocircuito, por lo que el segundo argumento no se verifica cuando no es necesario.

mira esto:

Console.WriteLine(b1 & b2); // outputs 1 
Console.WriteLine(b2 & b1); // outputs 1 

En realidad se está perdiendo un poco importante, que le permitirá utilizar MyStruct (con & y |) como un valor lógico - una conversión implícita a bool:

public static implicit operator bool(MyStruct ms) { 
    return ms._i > 0; 
} 

Esto le permite utilizar MyStruct (también el resultado de los operadores) como tal:

if (b1 & b2) 
    Console.WriteLine("foo"); 

Como el último, posiblemente más importante, nota: el problema en su ejemplo viene del hecho de que usted quiere hacer operaciones lógicas (comprobar si 2 casos de MyStruct son true), pero su operador & se implementa incorrectamente para tal propósitoFunciona en términos de aritmética binaria, produciendo una instancia de MyStruct con el valor 1 cuando se llama con los argumentos MyStruct(1) (true) y MyStruct(-1) (false). Entonces básicamente hace (true & false) == true. Esta es la razón por la cual b1 && b2 da un resultado diferente que b2 && b1 en su ejemplo. Cualquier lógica adicional basada en este operador será rota e impredecible. Comportamiento de &&, que se implementa en .NET en términos de false y &, lo confirma.

EDIT: Quiere ser capaz de utilizar MyStruct como booleano. Implementa los operadores true y false y espera que && y || funcionen en términos de lógica booleana. Sin embargo, se implementa & en términos de aritmética binaria (usando & en int campos), lo que hace que esta implementación de & no es compatible con la lógica booleana que esperas ((1 & -1) == 1, lo que significa (true & false) == false en su interpretación de MyStruct valor booleano). Ahora, considere que && en general no es un operador lógico (no devuelve un bool) - es un cortocircuito implementado como T.false(x) ? x : T.&(x, y). Tenga en cuenta que devuelve MyStruct en su caso, que acaba de interpretar como true o false en función del valor de su campo. La conclusión: espera que && haga una prueba lógica en ambos operandos, pero la implementación .NET de && usa su implementación de &, que no es compatible con la lógica booleana que espera.

+1

No necesito implementar el molde implícito a 'bool' para poder usar' if (b1 & b2) '. Simplemente ejecuta el operador 'true' en el resultado de' b1 & b2'. La única diferencia al implementar la sobrecarga es que 'Console.WriteLine (b1 && b2)' no imprime '1' o' -1', sino que en realidad 'True' o' False'. Si implemento el molde 'bool' y elimino el operador' & ', solo se usará' bool'. En ese caso, puedo eliminar las sobrecargas 'true' y' false', porque ya no se ejecutarán. – comecme

+1

Tienes razón, mi mal, no probé eso. También tienes razón en que si se implementa 'bool',' & ',' true' y 'false' se pueden eliminar (en este caso específico). Supongo que esto es lo que realmente necesitas, ya que 'MyStruct' se comporta de una manera lógicamente correcta en ese momento. Así que su búsqueda de una implementación útil de 'false' continúa ;-) –

0

El punto de los operadores verdadero/falso es proporcionar semántica de lógica booleana sin la necesidad de conversión implícita a bool.

Si permite que su tipo se emita implícitamente en bool, los operadores verdadero/falso ya no son necesarios. Pero si solo desea conversiones explícitas o ninguna conversión, pero aún desea permitir su tipo como condición en if o while expresssion, puede usar los operadores true y false.

Cuestiones relacionadas