2009-05-08 3 views
10

Я пытаюсь переопределить оператор равенства (==) в C# для обработки сравнения любого типа с пользовательский тип (пользовательский тип действительно обертка/поле вокруг нуля).Linq и оператор равенства: выражение типа «System.Int32» не может использоваться для параметра типа «System.Object»

Так у меня есть это:

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

Теперь, если я сделать вызов, как:

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

Он работает прекрасно. Однако, если я пытаюсь сделать это же самое через дерево выражения Linq:

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

Он бросает исключение:

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) 

Любые идеи о том, почему базовая система может преобразовывать Int32 в объект, но Linq не может, или как я могу это исправить?

Это все дело смотрел, потому что Linq также не может сравнить Int32 на объект в первую очередь:

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

Выдает исключение о том, что не существует оператор сравнения для «System.Int32» и «системы. Объект».


Быстрый Followup:

Следующая работают без проблем:

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

Так специально литье все, чтобы возразить. Так что Linq просто не обрабатывает наследование внутри? То очень раздражает ...


Followup # 2:

Я также попытался с помощью метода пользовательского сравнения:

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

Это тоже бросает исключение:

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) 

Но снова бросая все прямо на предметные работы:

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

Так что, я думаю, у меня есть обходной путь ... бросить все на объект и использовать собственный метод сравнения. Я все еще удивлен, что Linq не делает преобразование автоматически, как это делает обычный C#.

+6

«Так же Linq просто не справиться с наследования внутри? Thats очень раздражает ...» Да, это раздражает, но это по уважительной причине. Библиотеки дерева выражений работают с выражениями как с C#, так и с VB, и, если на то пошло, на любом другом языке, который имеет такие выражения. Если мы исправили правила преобразования C# в код, который обрабатывал разрешение равенства, то мы могли бы сделать неправильную вещь для выражений, которые пришли из VB. Таким образом, мы не делаем этого - вы должны передавать однозначные выражения, так что разрешение является языковым агностиком. –

+0

(добавлено запомнилось вашим комментарием) –

+0

Ну, вы не получите лучшего авторитета на эту тему, чем Эрик Липперт! –

ответ

9

Что не так с пустым? Re отсутствующего int против null, попробуйте int?:

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

Моя проблема в том, что я действительно не знаю типов. Expression.Constant динамически создается из значений в словаре . Проблема в том, что что-то не существует в словаре, оно возвращает null. – CodingWithSpike

+0

Чтобы прояснить мой вышеприведенный комментарий, кто-то может попытаться сравнить «x» и «y», и код будет захватывать «x» и «y» из Словаря и делать Expression.Constant (dict ["x" ]) и Expression.Constant (dict ["y"]) и попытайтесь их равно(). – CodingWithSpike

+1

Ну, использование «объекта» немного рискованно с выражением; он должен доказать, что типы используют правильные перегрузки ... вы могли бы, вероятно, ввести некоторые специальные правила, чтобы, если один операнд имеет значение null, вы используете тип из другого операнда. –