2009-05-08 14 views
10

Sto provando a sovrascrivere l'operatore di uguaglianza (==) in C# per gestire il confronto di qualsiasi tipo in un tipo personalizzato (il tipo personalizzato è in realtà un wrapper/box attorno a null).Linq e l'operatore di uguaglianza: espressione del tipo 'System.Int32' non può essere utilizzata per parametro di tipo 'System.Object'

Così ho questo:

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; 
    } 
    ... 
} 

Ora, se faccio una chiamata del tipo:

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

Funziona perfettamente bene. Tuttavia, se cerco di fare la stessa cosa attraverso un'espressione albero Linq:

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

Esso genera l'eccezione:

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) 

Delle idee su perché il sistema di base in grado di convertire Int32 di opporsi, ma Linq non può, o come posso risolvere questo?

Questa intera faccenda fissò perché Linq, inoltre, non si può paragonare Int32 per oggetto, in primo luogo:

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

genera un'eccezione che indica che non v'è alcun operatore di confronto per "System.Int32" e "Sistema. Oggetto".


followup rapida:

Il seguente fare il lavoro senza problema:

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) 
); 

Così specificamente colata di tutto per opporsi. Quindi Linq non gestisce l'ereditarietà internamente? Quello è abbastanza fastidioso ...


Followup # 2:

Ho anche provato ad utilizzare un metodo di confronto personalizzato:

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; 
    } 

Questo getta anche un'eccezione:

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) 

Ma di nuovo tutto direttamente su opere oggetto:

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

Quindi penso di avere la mia soluzione alternativa ... gettare tutto su oggetti e utilizzare un metodo di confronto personalizzato. Sono ancora sorpreso che Linq non esegua la conversione automaticamente come fa il normale C#.

+6

"Quindi Linq non gestisce l'ereditarietà internamente?Questo è piuttosto fastidioso ... "Sì, è fastidioso, ma è per una buona ragione: le librerie ad albero delle espressioni funzionano con espressioni sia di C# che di VB e, se è per questo, qualsiasi altra lingua che abbia quel tipo di espressioni. abbiamo infornato le regole di conversione C# nel codice che gestiva l'uguaglianza risolvente, quindi potremmo fare la cosa sbagliata per le espressioni provenienti da VB. Quindi non ne facciamo nessuna - è necessario passare espressioni non ambigue, in modo che la risoluzione sia la lingua -agnostico –

+0

(aggiunto pensato per il tuo commento) –

+0

Beh, non si otterrà una migliore autorità in materia di Eric Lippert! –

risposta

9

Cosa c'è di sbagliato con null? Re dei dispersi int vs null, provare int?:

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

My il problema è che in realtà non conosco i tipi. Expression.Constant viene costruito dinamicamente dai valori in un dizionario . Il problema è quando qualcosa non esiste il dizionario, restituisce null. – CodingWithSpike

+0

Per chiarire ulteriormente il mio commento sopra, qualcuno può provare a confrontare "x" e "y", e il codice afferrerà "x" e "y" da un dizionario e creerà un Expression.Constant (dict ["x" ]) e Expression.Constant (dict ["y"]) e prova a Equal() loro. – CodingWithSpike

+1

Bene, usare "oggetto" è un po 'rischioso con Expression; ha bisogno di dimostrare che i tipi usano i sovraccarichi corretti ... probabilmente si potrebbero inserire alcune regole speciali in modo che se un operando è nullo si usa il tipo dall'altro operando, comunque. –

Problemi correlati