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.