【问题标题】:Linq and the Equality Operator: Expression of type 'System.Int32' cannot be used for parameter of type 'System.Object'Linq 和相等运算符:“System.Int32”类型的表达式不能用于“System.Object”类型的参数
【发布时间】:2009-05-08 14:47:15
【问题描述】:

我正在尝试重写 C# 中的相等 (==) 运算符来处理任何类型与自定义类型的比较(自定义类型实际上是围绕 null 的包装器/框)。

所以我有这个:

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 转换为 Object,但 Linq 不能,或者我该如何解决这个问题的任何想法?

这整件事都盯着看,因为 Linq 一开始也无法将 Int32 与 Object 进行比较:

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

抛出一个异常,指出“System.Int32”和“System.Object”没有比较运算符。


快速跟进:

以下操作没有问题:

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 是否只是在内部不处理继承?好烦啊……


跟进#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# 那样自动进行转换感到惊讶。

【问题讨论】:

  • “那么 Linq 不只是在内部处理继承吗?这很烦人……” 是的,这很烦人,但这是有充分理由的。表达式树库适用于来自 C# 和 VB 的表达式,并且就此而言,适用于具有这些类型表达式的任何其他语言。如果我们将 C# 转换规则嵌入到处理解决相等性的代码中,那么我们可能会对来自 VB 的表达式做错事。所以我们都不做——你需要传递明确的表达式,这样解析就与语言无关。
  • (添加了您的评论)
  • 好吧,在这个主题上,你不会比 Eric Lippert 获得更好的权威!
  • @eric:感谢您的确认以及对 Linq 为何如此工作的一些见解。

标签: c# .net linq operator-overloading


【解决方案1】:

null 有什么问题?回复缺失的int vs null,试试int?

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

【讨论】:

  • 我的问题是我真的不知道类型。 Expression.Constant 是根据 Dictionary 中的值动态构建的。问题是当字典中不存在某些内容时,它会返回 null。
  • 为了进一步澄清我上面的评论,有人可以尝试比较“x”和“y”,代码将从字典中抓取“x”和“y”,并制作一个表达式。 Constant(dict["x"]) 和 Expression.Constant(dict["y"]) 并尝试 Equal() 它们。
  • 嗯,使用“object”对 Expression 来说有点冒险;它需要证明类型以使用正确的重载...您可能可以设置一些特殊规则,以便如果一个操作数为空,您可以使用来自另一个操作数的类型。
【解决方案2】:

你应该看看这篇文章来检查一个可以为空的类型。

http://msdn.microsoft.com/en-us/library/ms366789.aspx

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多