The operator '==' cannot be applied to operands of type 'T' and 'T'

In the code example below, the compiler complains about x.Id == reference.Id :

The operator '==' cannot be applied to operands of type "TId" and "TId"

Similar questions were asked on SO, and they are solved by replacing the == operator with IEquatable<> + Equals or EqualityComparer<TEnum>.Default .

However, both solutions do not work for me for reasons that are not important for this issue.

I am not looking for a replacement for the == operator, I am looking for an explanation why the equality operator does not work for generic types.

 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); } } 
0
generics c #
source share
3 answers

I am not looking for a replacement for the == operator, I am looking for an explanation why the compiler cannot understand that both common properties are of the same type.

There are no explanations explaining the falsity. The compiler can and finds out that both common properties have the same type of compilation time, which you could illustrate something like this:

 x.Id = reference.Id; 

The compiler will not give problems with this assignment, since it knows that there is an identical conversion between two identical during compilation types.

So you have to look for an explanation of some other thing. I think that what you are really looking for is an excuse for why allowing operator overloading does not allow you to find the best operator for equality in type parameter.

Answer: C # generic types are not C ++ templates. In C ++, if you have ex1 OP ex2 , then the operator permission, which determines its semantics, is executed once to build the template . In C #, we do not; we perform a one-time overload resolution for operators and must find an operator that works for all possible substitutions of type arguments.

We cannot do this for equality operators on unbounded types; if TId is an object , then reference equality must be satisfied; if it is a string, then equality of strings must be satisfied, if it is int, then equality of equality must be fulfilled, if it is "nullable Guid", then alignment by the principle of "null-to-nullable", etc. must be performed. In C #, there is no generalized equality operator, but only a set of specific equality operators , and since there is no generalized operator, there is no single operator overload selection operator. Thus, you get an error message.

That's why for this you usually limit the type to implement some interface that you can use; we can generally call interface methods for generic types.

You rejected this correct solution to the problem, and therefore we cannot do this to help you here, without knowing more about why you rejected the standard, safe and effective solution.

Now you may notice that the compiler can generate code that determines at runtime what the resolution of the overload resolution algorithm is, based on the types of runtime. C # cannot do this without excessive performance; if you are willing to pay this cost, then leave the operands to dynamic . This tells the compiler that you are ready to accept overload resolution errors at run time in exchange for not getting them at compile time; be careful! When you turn off the security system, you are responsible for ensuring the security of your program.

+5
source share

Since you are talking about problems with the Entity Framework, I can assume that your _store is actually not the same as List , as in your code example (you won’t have any problems using the other methods mentioned), but in some form of IQueryable . Then you can do what you want by creating Expression manually, for example:

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

You can apply the same method to your code sample, though by compiling the expression with Compile() and passing the Func result to FirstOrDefault , although I will not recommend doing this in practice if it is really necessary (let's say you want to actually call operator == and nothing else).

+1
source share

The compiler complains because it does not know whether the TId is a value type or not. If you can, adding a class constraint should make it work.

as

 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 ); } } 
-3
source share

All Articles