运营商&= 39; =='不能应用于' T'类型的操作数。和' T'

时间:2017-10-23 13:36:24

标签: c# generics

在下面的代码示例中,编译器在x.Id == reference.Id上抱怨:

  

运营商' =='不能应用于类型' TId'的操作数。和' TId'

在SO上提出了类似的问题,并通过== + IEquatable<>Equals替换EqualityComparer<TEnum>.Default - 运算符来解决这些问题。

然而,由于对此问题不重要的原因,这两种解决方案都不适合我。

我没有寻找==运营商的替代品,我正在寻找解释为什么相等运算符不适用于泛型类型。

public class Object<TId>
{
    public TId Id { get; set; }

    // Some other object properties...
}

public class ObjectReference<TId>
{
    public TId Id { get; set; }
}

public class ObjectStore<TId>
{
    private List<Object<TId>> _store = new List<Object<TId>>();

    public Object<TId> FindByReference(ObjectReference<TId> reference)
    {
        return _store.FirstOrDefault(x => x.Id == reference.Id);
    }
}

3 个答案:

答案 0 :(得分:7)

  

我没有找到==运算符的替代品,我正在寻找解释为什么编译器无法确定两个通用属性属于同一类型的解释。

没有任何解释可以解释虚假。编译器可以并且确实发现这两个泛型属性具有相同的编译时类型,您可以使用以下内容来说明:

x.Id = reference.Id;

编译器允许该分配没有问题,因为它知道在编译时两个相同的类型之间存在标识转换

所以你必须寻找其他东西的解释。我认为你真正想要的是为什么运算符重载解析无法找到类型参数上相等的最佳运算符的理由。

答案是:C#泛型类型不是C ++模板。在C ++中,如果你有ex1 OP ex2那么确定其语义的运算符的分辨率是在每个模板构造时执行一次。在C#中,我们不这样做;我们对运算符执行一次的重载决策,并且必须找到一个适用于所有可能的类型参数替换的运算符

对于无约束类型的相等运算符,我们不能这样做;如果TIdobject,则必须执行引用相等;如果它是字符串则必须执行字符串相等,如果它是int则必须执行int相等,如果它是&#34;可以为空的Guid&#34;,那么必须执行提升到可为空的Guid相等,等等。 C#中没有广义等式运算符,只有特定等式运算符的集合,并且由于没有广义运算符,因此没有单个运算符可供选择运算符重载决策。因此,您会收到错误。

这就是为什么为了做到这一点,你通常会限制类型来实现一些可以使用的接口;我们可以一般地调用泛型类型的接口方法。

您已经拒绝了解决问题的正确方法,因此我们无法帮助您,而又不了解您拒绝标准,安全,高效的解决方案的原因

现在,您可能会注意到编译器可以根据运行时类型生成代码,该代码在运行时确定 重载决策算法的分辨率。没有过高的性能成本,C#不能做到这一点;如果您愿意支付这笔费用,那么将您的操作数投放到dynamic 。这告诉编译器你愿意在运行时接受重载解析失败,以换取在编译时没有得到它们;小心!当您关闭安全系统时,您有责任确保程序的类型安全。

答案 1 :(得分:2)

由于您正在讨论实体框架的问题,我可以假设您的_store与示例代码中的List不一样(因为您在使用其他代码时遇到了问题)提到的方法)但某种形式的IQueryable。然后你应该能够通过手动构建Expression来做你想做的事情,如下所示:

public class ObjectStore<TId> {
    private readonly IQueryable<Object<TId>> _store;

    public ObjectStore(IQueryable<Object<TId>> store) {
        _store = store;
    }

    public Object<TId> FindByReference(ObjectReference<TId> reference) {
        var refId = reference.Id;
        // x =>
        var arg = Expression.Parameter(typeof(Object<TId>), "x");
        // x.Id == refId
        var equals = Expression.Equal(Expression.Property(arg, "Id"), Expression.Constant(refId));
        // x => x.Id == refId
        var where = (Expression<Func<Object<TId>, bool>>) Expression.Lambda(equals, arg);
        return _store.FirstOrDefault(where);
    }
}

您也可以将相同的方法应用于示例代码,方法是使用Compile()编译表达式并将结果Func传递给FirstOrDefault,但我不建议您这样做在实践中除非确实有必要(比如说你想真正调用==运算符而不是其他)。

答案 2 :(得分:-4)

编译器抱怨,因为它不知道TId是否是值类型。如果可以的话,设置类约束应该可以使它工作

public class Object<TId> where TId: class {
    public TId Id { get; set; }

    // Some other object properties...
}

public class ObjectReference<TId> where TId : class {
    public TId Id { get; set; }
}

public class ObjectStore<TId> where TId : class {
    private List<Object<TId>> _store = new List<Object<TId>>( );

    public Object<TId> FindByReference( ObjectReference<TId> reference ) {
        return _store.FirstOrDefault( x => x.Id == reference.Id );
    }
}