在下面的代码示例中,编译器会抱怨x.Id == reference.Id
。
运算符'==‘不能应用于'TId’和'TId‘类型的操作数
类似的问题也被问到了,它们通过用==
+ Equals
或EqualityComparer<TEnum>.Default
代替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);
}
}
发布于 2017-10-23 18:04:03
我不是在寻找替代==运算符的方法,而是要解释为什么编译器无法理解这两种泛型属性的类型是相同的。
没有任何解释可以解释谎言。编译器能够并且确实发现这两个泛型属性都是相同的编译时类型,您可以用如下所示的方法来说明这一点:
x.Id = reference.Id;
编译器将允许该赋值没有问题,因为它知道在编译时类型相同的两个类型之间存在身份转换。
所以你一定是在找其他事情的解释。我认为您真正要寻找的是为什么运算符重载解析不能在类型参数上找到最佳的相等运算符的理由。
答案是: C#泛型类型不是C++模板。在C++中,如果您有ex1 OP ex2
,那么确定其语义的操作符的分辨率将在模板的每个构造中执行一次。在C#中,我们不会这样做;我们只对操作符执行一次重载解析,并且必须找到一个可以用于所有类型参数替换的操作符。
对于无约束类型上的相等操作符,我们不能这样做;如果TId
是object
,则必须执行引用相等;如果它是字符串,则必须执行字符串相等,如果它是int,则必须执行int相等,如果它是“可为空的Guid",则必须执行从空到可空的Guid相等,依此类推。--在C#中没有广义等式运算符,只有一组特定的等式操作符,而且由于没有广义运算符,所以没有一个操作符可供选择。因此,您将得到一个错误。
这就是为什么为了做到这一点,您通常会限制类型以实现一些可以使用的接口;我们可以在泛型类型上通用地调用接口方法。
你已经拒绝了这个问题的正确解决方案,所以在不知道为什么你拒绝标准、安全、有效的解决方案的情况下,我们没有什么可以帮助你的。
现在,您可能会注意到,编译器可以生成代码,在运行时根据运行时类型确定重载解析算法的分辨率。C#不能在不增加性能成本的情况下做到这一点;如果您愿意支付该成本,那么将操作数转换为dynamic
。这告诉编译器,您愿意在运行时接受重载解析失败,而不是在编译时获得它们;请小心!当您关闭安全系统时,您有责任确保程序的类型安全。
发布于 2017-10-23 14:57:34
由于您讨论的是实体框架的问题,我可以假设您的_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
,不过我不建议在实践中这样做,除非真的有必要(假设您真的想调用==
操作符,其他什么都不需要)。
https://stackoverflow.com/questions/46890757
复制相似问题