2009-05-08 9 views
10

Próbuję przesłonić operatora równości (==) w języku C#, aby poradzić sobie z porównywaniem dowolnego typu z typ niestandardowy (typ niestandardowy to tak naprawdę opakowanie/pudełko o wartości zerowej).Linq i operator równości: wyrażenie typu "System.Int32" nie może być używane dla parametru typu "System.Object"

Więc mam to:

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

Teraz gdybym wykonać połączenie jak:

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

to działa perfekcyjnie. Jednakże, jeśli staram się robić to samo przez LINQ drzewa wyrażenie:

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

To rzuca wyjątek:

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) 

Wszelkie pomysły, dlaczego system bazowy może konwertować Int32 do obiektu, ale LINQ nie mogę lub jak mogę to naprawić?

To wszystko patrzył bo Linq też nie można porównywać Int32 do obiektu w pierwszej kolejności:

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

Zgłasza wyjątek, stwierdzając, że nie ma operatora porównania dla „System.Int32” i „system. Obiekt".


Szybkie nawiązanie:

Poniższy zrobić pracę bez problemu:

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

Więc specjalnie rzucając wszystko sprzeciwu. Czy Linq po prostu nie zajmuje się dziedziczeniem wewnętrznie? Ów dość irytujące ...


Followup # 2:

Próbowałem też stosując metodę porównawczą niestandardowe:

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

To też zgłasza wyjątek:

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) 

Ale ponownie rzucając wszystko bezpośrednio na obiekt działa:

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

Sądzę, że mam moje obejście ... rzucić wszystko do obiektu i użyć niestandardowej metody porównania. Nadal jestem zaskoczony, że Linq nie dokonuje konwersji automatycznie, tak jak robi to normalne C#.

+6

"Czy Linq po prostu nie zajmuje się dziedziczeniem wewnętrznie?To dość denerwujące ... "Tak, jest to denerwujące, ale nie bez powodu: biblioteki drzewek wyrażeń działają z wyrażeń zarówno z C#, jak i VB, i, o ile to możliwe, z każdego innego języka, który ma tego rodzaju wyrażenia. upiekliśmy zasady konwersji C# na kod, który zajmował się rozwiązywaniem równości, a następnie moglibyśmy zrobić coś niewłaściwego dla wyrażeń, które pochodziły z VB, więc nie robimy żadnego - musisz podać jednoznaczne wyrażenia, aby rozdzielczość była językiem . -agnostic –

+0

(dodane myśl re swoim komentarzu) –

+0

Cóż, nie będzie lepiej autorytet w tej sprawie niż Eric Lippert –

Odpowiedz

9

Co jest nie tak z wartością zerową? Re brakującej int vs null, spróbuj int?:

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

My Problem polega na tym, że naprawdę nie znam typów. Expression.Constant jest budowany dynamicznie z wartości w Słowniku <łańcuch, obiekt>. Kwestia jest, gdy coś nie istnieje w słownik, zwraca wartość null. – CodingWithSpike

+0

Aby dokładniej wyjaśnić powyższy komentarz, ktoś może spróbować porównać "x" i "y", a kod pobierze słowa "x" i "y" ze Słownika i stworzy Expression.Constant (dict ["x"] ]) i Expression.Constant (dict ["y"]) i spróbuj Equal() je. – CodingWithSpike

+1

Cóż, użycie "obiektu" jest nieco ryzykowne w wyrażeniu; musi udowodnić, że typy używają poprawnych przeciążeń ... prawdopodobnie mógłbyś wprowadzić pewne specjalne reguły, więc jeśli jeden operand ma wartość null, użyjesz tego typu z drugiego argumentu. –

Powiązane problemy