2009-05-08 33 views
10

Estoy intentando anular el operador de igualdad (==) en C# para manejar la comparación de cualquier tipo un tipo personalizado (el tipo personalizado es realmente un contenedor/caja alrededor de nulo).Linq and the Equality Operator: Expresión del tipo 'System.Int32' no se puede usar para el parámetro de tipo 'System.Object'

así que tengo esto:

internal sealed class Nothing 
{ 
    public override bool Equals(object obj) 
    { 
     if (obj == null || obj is Nothing) 
      return true; 
     else 
      return false; 
    } 

    public static bool operator ==(object x, Nothing y) 
    { 
     if ((x == null || x is Nothing) && (y == null || y is Nothing)) 
      return true; 
     return false; 
    } 
    ... 
} 

Ahora bien, si hago una llamada como:

Nothing n = new Nothing(); 
bool equal = (10 == n); 

Funciona perfectamente bien. Sin embargo, si trato de hacer esto mismo a través de un LINQ árbol de expresión:

exp = Expression.Equal(
    Expression.Constant(10), 
    Expression.Constant(new Nothing(), typeof(Nothing)) 
); 

Se inicia la excepción:

System.ArgumentException : Expression of type 'System.Int32' cannot be used for parameter of type 'System.Object' of method 'Boolean op_Equality(System.Object, PARTSFinder.Rules.Runtime.RulesNothing)' 
    at System.Linq.Expressions.Expression.ValidateArgumentTypes(MethodInfo method, ReadOnlyCollection`1& arguments) 
    at System.Linq.Expressions.Expression.ValidateCallArgs(Expression instance, MethodInfo method, ReadOnlyCollection`1& arguments) 
    at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, IEnumerable`1 arguments) 
    at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, Expression[] arguments) 
    at System.Linq.Expressions.ExpressionCompiler.GenerateBinaryMethod(ILGenerator gen, BinaryExpression b, StackType ask) 

¿Alguna idea de por qué el sistema base puede convertir Int32 al objeto, pero LINQ no puede, o cómo puedo arreglar esto?

Esta cosa entera se quedó porque LINQ también no se puede comparar a Int32 objeto en primer lugar:

exp = Expression.Equal(
    Expression.Constant(10), 
    Expression.Constant(null) 
); 

lanza una excepción que indica que no hay ningún operador de comparación para "System.Int32" y "Sistema. Objeto".


seguimiento rápida:

La siguiente hacer el trabajo sin problemas:

exp = Expression.Equal(
    Expression.Constant(10, typeof(object)), 
    Expression.Constant(new Nothing(), typeof(Nothing)) 
); 

exp = Expression.Equal(
    Expression.Constant(10, typeof(object)), 
    Expression.Constant(null) 
); 

Así fundición específicamente todo lo que objetar. Entonces, ¿Linq simplemente no maneja la herencia internamente? Eso es bastante molesto ...


Seguimiento # 2:

También he intentado usar un método de comparación de encargo:

exp = Expression.Equal(
    Expression.Constant(10), 
    Expression.Constant(null), 
    false, 
    this.GetType().GetMethod("ValueEquals", BindingFlags.Public | BindingFlags.Static) 
); 

    public static bool ValueEquals(object x, object y) 
    { 
     if (x == null && y == null) 
      return true; 
     if (x.GetType() != y.GetType()) 
      return false; 
     return x == y; 
    } 

Esto también se produce una excepción:

System.InvalidOperationException : The operands for operator 'Equal' do not match the parameters of method 'ValueEquals'. 
    at System.Linq.Expressions.Expression.GetMethodBasedBinaryOperator(ExpressionType binaryType, Expression left, Expression right, MethodInfo method, Boolean liftToNull) 

Pero volviendo a enviar todo directamente a las obras objetadas:

exp = Expression.Equal(
    Expression.Constant(10, typeof(object)), 
    Expression.Constant(null, typeof(object)), 
    false, 
    this.GetType().GetMethod("ValueEquals", BindingFlags.Public | BindingFlags.Static) 
); 

Así que supongo que tengo mi solución alternativa ... enviar todo para objetar y usar un método de comparación personalizado. Todavía estoy sorprendido de que Linq no haga la conversión automáticamente como lo hace C# normal.

+6

"Entonces, ¿LINQ simplemente no maneja internamente la herencia?Eso es bastante molesto ... "Sí, es molesto, pero es por una buena razón. Las bibliotecas de árbol de expresiones funcionan con expresiones de C# y VB, y, para el caso, cualquier otro lenguaje que tenga ese tipo de expresiones. preparamos las reglas de conversión de C# en el código que manejaba la resolución de igualdad, entonces podríamos estar haciendo lo incorrecto con las expresiones que venían de VB. Por lo tanto, no hacemos ninguna de las dos: se requiere pasar expresiones no ambiguas para que la resolución sea lenguaje -agnostic. –

+0

(se ha añadido el pensamiento a tu comentario) –

+0

¡Bueno, no obtendrás una mejor autoridad sobre el tema que Eric Lippert! –

Respuesta

9

¿Qué pasa con null? Re la falta int vs null, tratar int?:

exp = Expression.Equal(
    Expression.Constant(10, typeof(int?)), 
    Expression.Constant(null, typeof(int?)) 
); 
+0

Mi El problema es que realmente no conozco los tipos. El Expression.Constant se está construyendo dinámicamente a partir de los valores en un Dictionary . El problema es cuando algo no existe en el diccionario, devuelve nulo. – CodingWithSpike

+0

Para aclarar aún más mi comentario anterior, alguien puede intentar comparar "x" e "y", y el código tomará "x" e "y" de un diccionario, y hará un Expression.Constant (dict ["x"] ]) y Expression.Constant (dict ["y"]) y tratar de Equal() ellos. – CodingWithSpike

+1

Bueno, usar "objeto" es un poco arriesgado con Expression; necesita probar los tipos para usar las sobrecargas correctas ... usted probablemente podría poner algunas reglas especiales para que si un operando es nulo, use el tipo del otro operando. –

Cuestiones relacionadas