¿Para qué sirve el operador falso en C #?

107

Hay dos operadores extraños en C #:

Si entiendo bien, estos operadores se pueden usar en tipos que quiero usar en lugar de una expresión booleana y donde no quiero proporcionar una conversión implícita a bool.

Digamos que tengo la siguiente clase:

    public class MyType
    {
        public readonly int Value;

        public MyType(int value)
        {
            Value = value;
        }

        public static bool operator true (MyType mt)
        {
            return  mt.Value > 0;
        }

        public static bool operator false (MyType mt)
        {
            return  mt.Value < 0;
        }

    }

Entonces puedo escribir el siguiente código:

    MyType mTrue = new MyType(100);
    MyType mFalse = new MyType(-100);
    MyType mDontKnow = new MyType(0);

    if (mTrue)
    {
         // Do something.
    }

    while (mFalse)
    {
        // Do something else.
    }

    do
    {
        // Another code comes here.
    } while (mDontKnow)

Sin embargo, para todos los ejemplos anteriores, solo se ejecuta el operador verdadero. Entonces, ¿para qué sirve el operador falso en C #?

Nota: se pueden encontrar más ejemplos aquí , aquí y aquí .

Jakub Šturc
fuente
Los documentos modernos se pueden encontrar en docs.microsoft.com/en-us/dotnet/csharp/language-reference/… . Los documentos sugieren que ya no hay razón para definir este operador ya que C # 2.0 agregó tipos que aceptan valores NULL.
Mark Amery

Respuestas:

65

Puede usarlo para anular los operadores &&y ||.

Las &&y ||los operadores no pueden ser anulados, pero si se reemplaza |, &, truey falseexactamente de la manera correcta el compilador llamarán |y &cuando se escriba ||y &&.

Por ejemplo, mire este código (de http://ayende.com/blog/1574/nhibernate-criteria-api-operator-overloading - donde descubrí este truco; versión archivada por @BiggsTRC):

public static AbstractCriterion operator &(AbstractCriterion lhs, AbstractCriterion rhs)
{
       return new AndExpression(lhs, rhs);
}

public static AbstractCriterion operator |(AbstractCriterion lhs, AbstractCriterion rhs)
{
       return new OrExpression(lhs, rhs);
}

public static bool operator false(AbstractCriterion criteria)
{
       return false;
}
public static bool operator true(AbstractCriterion criteria)
{
       return false;
}

Obviamente, este es un efecto secundario y no la forma en que se pretende que se use, pero es útil.

Nir
fuente
su enlace es 404. No estoy seguro de cómo desea editar esto, así que lo dejé.
user7116
Actualicé la URL con una copia archivada para que aún se pueda leer.
IAmTimCorey
25

Shog9 y Nir: gracias por tus respuestas. Esas respuestas me señalaron el artículo de Steve Eichert y me señalaron msdn :

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, primero se evalúa x y se invoca el operador falso 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 el operador seleccionado & se invoca en el valor calculado previamente para x y el valor calculado para y para producir el resultado de la operación.

Jakub Šturc
fuente
Me pregunto por qué no han optado por utilizar la (!T.true(x)) ? x : T.&(x, y)lógica.
Des Nerger
14

La página a la que enlaza http://msdn.microsoft.com/en-us/library/6x6y6z4d.aspx dice para qué eran, que era una forma de manejar bools que aceptan valores NULL antes de que se introdujeran los tipos de valores que aceptan valores NULL.

Supongo que hoy en día son buenos para el mismo tipo de cosas que ArrayList, es decir, absolutamente nada.

Will Dean
fuente
7

AFAIK, se usaría en una prueba de falso, como cuando el &&operador entra en juego. Recuerde, && cortocircuitos, por lo que en la expresión

if ( mFalse && mTrue) 
{
   // ... something
}

mFalse.false()se llama, y ​​al devolver truela expresión se reduce a una llamada a 'mFalse.true ()' (que luego debería regresar false, o las cosas se pondrán raras).

Tenga en cuenta que debe implementar el &operador para que esa expresión se compile, ya que se usa si mFalse.false()devuelve false.

Shog9
fuente
3

Parece que el artículo de MSDN al que lo vinculó se proporcionó para permitir tipos booleanos que aceptan valores NULL antes de que el tipo Nullable (es decir, int ?, bool ?, etc.) se introduzca en el lenguaje en C # 2. Por lo tanto, almacenaría un valor interno que indique si el valor es verdadero, falso o nulo, es decir, en su ejemplo> 0 para verdadero, <0 para falso y == 0 para nulo, y luego obtendría una semántica nula de estilo SQL. También tendría que implementar un método o propiedad .IsNull para que la nulidad se pueda verificar explícitamente.

En comparación con SQL, imagine una tabla Tabla con 3 filas con el valor Foo establecido en verdadero, 3 filas con el valor Foo establecido en falso y 3 filas con el valor Foo establecido en nulo.

SELECT COUNT(*) FROM Table WHERE Foo = TRUE OR Foo = FALSE
6

Para contar todas las filas, tendría que hacer lo siguiente: -

SELECT COUNT(*) FROM Table WHERE Foo = TRUE OR Foo = FALSE OR Foo IS NULL
9

Esta sintaxis 'IS NULL' tendría un código equivalente en su clase como .IsNull ().

LINQ hace que la comparación con C # sea aún más clara: -

int totalCount = (from s in MyTypeEnumerable
                 where s || !s
                 select s).Count();

Imaginando que MyTypeEnumberable tiene exactamente el mismo contenido de la base de datos, es decir, 3 valores iguales a verdadero, 3 valores iguales a falso y 3 valores iguales a nulo. En este caso, totalCount se evaluaría a 6 en este caso. Sin embargo, si reescribimos el código como: -

int totalCount = (from s in MyTypeEnumerable
                 where s || !s || s.IsNull()
                 select s).Count();

Entonces totalCount evaluaría a 9.

El ejemplo de DBNull proporcionado en el artículo vinculado de MSDN sobre el operador falso demuestra una clase en el BCL que tiene este comportamiento exacto.

En efecto, la conclusión es que no debe usar esto a menos que esté completamente seguro de que desea este tipo de comportamiento, ¡es mejor usar la sintaxis anulable mucho más simple!

Actualización: ¡Acabo de notar que necesitas anular manualmente los operadores lógicos!, || y && para que esto funcione correctamente. Creo que el operador falso se alimenta de estos operadores lógicos, es decir, indica la verdad, la falsedad o "lo contrario". Como se señaló en otro comentario,! X no funcionará desde el principio; ¡tienes que sobrecargar! ¡Rareza!

ljs
fuente