float - operator c#
¿Por qué los boles nulables no permiten if(anulables) pero permiten if(nullable== true)? (6)
Este código compila:
static void Main(string[] args)
{
bool? fred = true;
if (fred == true)
{
Console.WriteLine("fred is true");
}
else if (fred == false)
{
Console.WriteLine("fred is false");
}
else
{
Console.WriteLine("fred is null");
}
}
Este código no se compila.
static void Main(string[] args)
{
bool? fred = true;
if (fred)
{
Console.WriteLine("fred is true");
}
else if (!fred)
{
Console.WriteLine("fred is false");
}
else
{
Console.WriteLine("fred is null");
}
}
Pensé que si (booleanExpression == true) se suponía que era una redundancia. ¿Por qué no es en este caso?
Porque Fred no es un booleano. es una estructura, que tiene una propiedad booleana llamada IsNull, o HasValue, o lo que sea ... El objeto llamado fred es el objeto compuesto complejo que contiene un booleano y un valor, no un booleano primitivo en sí ...
A continuación, por ejemplo, se podría implementar un Nullable Int. El Nullable genérico casi seguramente se implementa de manera similar (pero genéricamente). Aquí puede ver cómo se implementan las conversiones implícitas y explícitas.
public struct DBInt
{
// The Null member represents an unknown DBInt value.
public static readonly DBInt Null = new DBInt();
// When the defined field is true, this DBInt represents a known value
// which is stored in the value field. When the defined field is false,
// this DBInt represents an unknown value, and the value field is 0.
int value;
bool defined;
// Private instance constructor. Creates a DBInt with a known value.
DBInt(int value)
{
this.value = value;
this.defined = true;
}
// The IsNull property is true if this DBInt represents an unknown value.
public bool IsNull { get { return !defined; } }
// The Value property is the known value of this DBInt, or 0 if this
// DBInt represents an unknown value.
public int Value { get { return value; } }
// Implicit conversion from int to DBInt.
public static implicit operator DBInt(int x)
{ return new DBInt(x); }
// Explicit conversion from DBInt to int. Throws an exception if the
// given DBInt represents an unknown value.
public static explicit operator int(DBInt x)
{
if (!x.defined) throw new InvalidOperationException();
return x.value;
}
public static DBInt operator +(DBInt x)
{ return x; }
public static DBInt operator -(DBInt x)
{ return x.defined? -x.value: Null; }
public static DBInt operator +(DBInt x, DBInt y)
{
return x.defined && y.defined?
x.value + y.value: Null;
}
public static DBInt operator -(DBInt x, DBInt y)
{
return x.defined && y.defined?
x.value - y.value: Null;
}
public static DBInt operator *(DBInt x, DBInt y)
{
return x.defined && y.defined?
x.value * y.value: Null;
}
public static DBInt operator /(DBInt x, DBInt y)
{
return x.defined && y.defined?
x.value / y.value: Null;
}
public static DBInt operator %(DBInt x, DBInt y)
{
return x.defined && y.defined?
x.value % y.value: Null;
}
public static DBBool operator ==(DBInt x, DBInt y)
{
return x.defined && y.defined?
x.value == y.value: DBBool.Null;
}
public static DBBool operator !=(DBInt x, DBInt y)
{
return x.defined && y.defined?
x.value != y.value: DBBool.Null;
}
public static DBBool operator >(DBInt x, DBInt y)
{
return x.defined && y.defined?
x.value > y.value: DBBool.Null;
}
public static DBBool operator <(DBInt x, DBInt y)
{
return x.defined && y.defined?
x.value < y.value: DBBool.Null;
}
public static DBBool operator >=(DBInt x, DBInt y)
{
return x.defined && y.defined?
x.value >= y.value: DBBool.Null;
}
public static DBBool operator <=(DBInt x, DBInt y)
{
return x.defined && y.defined?
x.value <= y.value: DBBool.Null;
}
public override bool Equals(object o)
{
try { return (bool) (this == (DBInt) o); }
catch { return false; }
}
public override int GetHashCode()
{ return (defined)? value: 0; }
public override string ToString()
{ return (defined)? .ToString(): "DBInt.Null"; }
}
La declaración Nullable<bool> == true
está implícitamente comprobando Nullable<bool> == (Nullable<bool>)true
.
Tenga en cuenta que Nullable<bool>
sí no es un booleano. Es un contenedor para un booleano que también se puede configurar como nulo.
No hay conversión implícita de Nullable<bool>
a bool
. Hay una conversión implícita de bool
a Nullable<bool>
y eso es lo que ocurre (en términos de lenguaje) para cada una de las constantes de bool en la primera versión. Entonces se aplica el bool operator==(Nullable<bool>, Nullable<bool>
operador. (Esto no es lo mismo que otros operadores levantados - el resultado es simplemente bool
, no Nullable<bool>
).
En otras palabras, la expresión ''fred == false'' es de tipo bool
, mientras que la expresión ''fred'' es de tipo Nullable<bool>
por lo que no puede usarlo como la expresión "if".
EDITAR: para responder a los comentarios, nunca hay una conversión implícita de Nullable<T>
a T
y por una buena razón: las conversiones implícitas no deben arrojar excepciones, y a menos que desee que null
se convierta implícitamente en default(T)
no hay mucho de lo contrario, podría hacerse.
Además, si hubiera conversiones implícitas en ambos sentidos, una expresión como "nulable + no nulable" sería muy confusa (para los tipos que admiten +, como int
). Ambos + (T? T?) Y + (T, T) estarían disponibles, dependiendo de qué operando se convirtieron, ¡pero los resultados podrían ser muy diferentes!
Estoy 100% detrás de la decisión de tener solo una conversión explícita de Nullable<T>
a T
Si lanzas fred a boolean, compilará:
if (( bool )fred )
(...)
Creo que cuando comparas bool? bool, el compilador crea un molde implicito, hace la comparación y luego devuelve verdadero o falso. Resultado: la expresión evalúa a bool.
Cuando no se compara bool? a algo, la expresión evaluar a un bool ?, que es ilegal allí.
Técnicamente, la prueba condicional simple no requiere una conversión implícita a bool si tiene una implementación del operador verdadero.
bool? nullableBool = null;
SqlBoolean sqlBoolean = SqlBoolean.Null;
bool plainBool = sqlBoolean; // won''t compile, no implicit conversion
if (sqlBoolean) { } // will compile, SqlBoolean implements true operator
La pregunta original es buscar una implementación de null de estilo SQL donde null se trate más como un desconocido, mientras que la implementación de Nullable se parece más a agregar null como un posible valor extra. Por ejemplo compare:
if (((int?)null) != 0) { } //block will execute since null is "different" from 0
if (SqlInt32.Null != 0) { } // block won''t execute since "unknown" might have value 0
El comportamiento similar a la base de datos está disponible en los tipos de System.Data.SqlTypes
El problema de implementación está perfectamente establecido al decir: Fred
es de tipo Nullable<bool>
y el !
el operador no está definido para Nullable<bool>
. No hay ninguna razón por la cual !
operador en Nullable<bool>
debe definirse en términos de bool
.
Citando a Microsoft:
Al realizar comparaciones con tipos anulables, si uno de los tipos anulables es nulo, la comparación siempre se evalúa como falsa.
La regla no menciona la conversión implícita. Es solo una convención arbitraria que pretende garantizar que ninguna expresión booleana tenga excepciones. Una vez que la regla está en su lugar, sabemos cómo escribir el código. Lamentablemente, Microsoft se perdió este operador unario. Para ser coherente con el comportamiento de los operadores binarios, el siguiente código debería tener una final feliz.
Por lo tanto
static void Main(string[] args)
{
bool? fred = null;
if (!fred)
{
Console.WriteLine("you should not see this");
}
else
{
Console.WriteLine("Microsoft fixed this in 4.5!!!");
}
}
Apuesto a que hay programadores que ahora tienen que escribir fred==false
mientras que Microsoft soluciona este problema aparentemente nulo.