que - operador new c#
¿Por qué las estructuras genéricas y no genéricas se tratan de manera diferente cuando se construye una expresión que hace que el operador== sea anulable? (2)
Esto parece un error al levantar a cero los operandos en estructuras genéricas.
Considere la siguiente estructura ficticia, que anula el operator== :
struct MyStruct
{
private readonly int _value;
public MyStruct(int val) { this._value = val; }
public override bool Equals(object obj) { return false; }
public override int GetHashCode() { return base.GetHashCode(); }
public static bool operator ==(MyStruct a, MyStruct b) { return false; }
public static bool operator !=(MyStruct a, MyStruct b) { return false; }
}
Ahora considera las siguientes expresiones:
Expression<Func<MyStruct, MyStruct, bool>> exprA =
(valueA, valueB) => valueA == valueB;
Expression<Func<MyStruct?, MyStruct?, bool>> exprB =
(nullableValueA, nullableValueB) => nullableValueA == nullableValueB;
Expression<Func<MyStruct?, MyStruct, bool>> exprC =
(nullableValueA, valueB) => nullableValueA == valueB;
Los tres compilan y corren como se espera.
Cuando se compilan (usando .Compile() ) producen el siguiente código (parafraseado al inglés de la IL):
La primera expresión que toma solo
MyStruct(no anulables), simplemente llamaop_Equality(nuestra implementación deoperator ==)La segunda expresión, cuando se compila, produce un código que verifica cada argumento para ver si tiene
HasValue. Si ambos no lo hacen (ambosnulliguales), devuelvetrue. Si solo uno tiene un valor, devuelvefalse. De lo contrario, llama aop_Equalityen los dos valores.La tercera expresión comprueba el argumento anulable para ver si tiene un valor; de lo contrario, devuelve falso. De lo contrario, llama
op_Equality.
Hasta ahora tan bueno.
Paso siguiente: haga exactamente lo mismo con un tipo genérico: cambie MyStruct a MyStruct<T> en todas partes en la definición del tipo, y cámbielo a MyStruct<int> en las expresiones.
Ahora la tercera expresión compila pero lanza una excepción InvalidOperationException tiempo de ejecución con el siguiente mensaje:
Los operandos para el operador ''Igual'' no coinciden con los parámetros del método ''op_Equality''.
Yo esperaría que las estructuras genéricas se comportaran exactamente igual que las no genéricas, con todo el levantamiento de nulos descrito anteriormente.
Así que mis preguntas son:
- ¿Por qué hay una diferencia entre las estructuras genéricas y no genéricas?
- ¿Cuál es el significado de esta excepción?
- ¿Es este un error en C # / .NET?
El código completo para reproducir esto está disponible en esta lista .
La respuesta corta es: sí, eso es un error. He puesto un repro mínimo y un breve análisis a continuación.
Mis disculpas. Escribí mucho de ese código y probablemente fue mi error.
He enviado un informe a los equipos de desarrollo, pruebas y gestión de programas de Roslyn. Dudo que esto se reproduzca en Roslyn, pero verificarán que no, y decidirán si esto hace que la barra de un paquete de servicio C # 5 sea la correcta.
Siéntase libre de ingresar un problema en connect.microsoft.com también si quiere que sea rastreado allí también.
Repro minimo:
using System;
using System.Linq.Expressions;
struct S<T>
{
public static bool operator ==(S<T> a, S<T> b) { return false; }
public static bool operator !=(S<T> a, S<T> b) { return false; }
}
class Program
{
static void Main()
{
Expression<Func<S<int>?, S<int>, bool>> x = (a, b) => a == b;
}
}
El código que se genera en la reproducción mínima es equivalente a
ParameterExpression pa = Expression.Parameter(typeof(S<int>?), "a");
ParameterExpression pb = Expression.Parameter(typeof(S<int>), "b");
Expression.Lambda<Func<S<int>?, S<int>, bool>>(
Expression.Equal(pa, pb, false, infoof(S<int>.op_Equality)
new ParameterExpression[2] { pa, pb } );
Donde infoof es un operador falso que obtiene un MethodInfo para el método dado.
El código correcto sería:
ParameterExpression pa = Expression.Parameter(typeof(S<int>?), "a");
ParameterExpression pb = Expression.Parameter(typeof(S<int>), "b");
Expression.Lambda<Func<S<int>?, S<int>, bool>>(
Expression.Equal(pa, Expression.Convert(pb, typeof(S<int>?), false, infoof(S<int>.op_Equality)
new ParameterExpression[2] { pa, pb } );
El método Equal no puede tratar con un operando que acepta valores nulos, uno que no acepta nulos. Requiere que ambos sean anulables o que ninguno sea anulable.
(Tenga en cuenta que lo false es correcto. Este booleano controla si el resultado de una igualdad levantada es un booleano levantado; en C # no lo es, en VB lo es).
Sí, este error desapareció en Roslyn (el compilador en desarrollo). Veremos sobre el producto existente.