397

According to the documentation of the == operator in MSDN,

For predefined value types, the equality operator (==) returns true if the values of its operands are equal, false otherwise. For reference types other than string, == returns true if its two operands refer to the same object. For the string type, == compares the values of the strings. User-defined value types can overload the == operator (see operator). So can user-defined reference types, although by default == behaves as described above for both predefined and user-defined reference types.

So why does this code snippet fail to compile?

bool Compare<T>(T x, T y) { return x == y; }

I get the error Operator '==' cannot be applied to operands of type 'T' and 'T'. I wonder why, since as far as I understand the == operator is predefined for all types?

Edit: Thanks, everybody. I didn't notice at first that the statement was about reference types only. I also thought that bit-by-bit comparison is provided for all value types, which I now know is not correct.

But, in case I'm using a reference type, would the == operator use the predefined reference comparison, or would it use the overloaded version of the operator if a type defined one?

Edit 2: Through trial and error, we learned that the == operator will use the predefined reference comparison when using an unrestricted generic type. Actually, the compiler will use the best method it can find for the restricted type argument, but will look no further. For example, the code below will always print true, even when Test.test<B>(new B(), new B()) is called:

class A { public static bool operator==(A x, A y) { return true; } }
class B : A { public static bool operator==(B x, B y) { return false; } }
class Test { void test<T>(T a, T b) where T : A { Console.WriteLine(a == b); } }
Hosam Aly
  • 41,555
  • 36
  • 141
  • 182
  • It might be useful to understand that even without generics, there are some types for which the `==` is not allowed between two operands of the same type. This is true for `struct` types (except "pre-defined" types) which do not overload the `operator ==`. As a simple example, try this: `var map = typeof(string).GetInterfaceMap(typeof(ICloneable)); Console.WriteLine(map == map); /* compile-time error */` – Jeppe Stig Nielsen Aug 19 '13 at 08:54
  • Continuing my own old comment. For example (see [other thread](https://stackoverflow.com/questions/6379915/)), with `var kvp1 = new KeyValuePair(); var kvp2 = kvp1;`, then you cannot check `kvp1 == kvp2` because `KeyValuePair<,>` is a struct, it is not a C# pre-defined type, and it does not overload the `operator ==`. Yet an example is given by `var li = new List(); var e1 = li.GetEnumerator(); var e2 = e1;` with which you cannot do `e1 == e2` (here we have the nested struct `List<>.Enumerator` (called ``"List`1+Enumerator[T]"`` by the runtime) which does not overload `==`). – Jeppe Stig Nielsen Jun 18 '17 at 14:45
  • Equally valuable almost-duplicate (so not voting to close): [Null or default comparison of generic argument in C#](https://stackoverflow.com/q/65351/11683) – GSerg Jun 14 '19 at 17:53

14 Answers14

352

As others have said, it will only work when T is constrained to be a reference type. Without any constraints, you can compare with null, but only null - and that comparison will always be false for non-nullable value types.

Instead of calling Equals, it's better to use an IComparer<T> - and if you have no more information, EqualityComparer<T>.Default is a good choice:

public bool Compare<T>(T x, T y)
{
    return EqualityComparer<T>.Default.Equals(x, y);
}

Aside from anything else, this avoids boxing/casting.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Minor aside, Jon; you might want to note the comment re pobox vs yoda on my post. – Marc Gravell Dec 24 '08 at 12:38
  • 4
    Nice tip on using EqualityComparer – chakrit Aug 23 '11 at 13:01
  • Great answer, it also answers a question I had (12 years after you posted your answer ;-)) - I needed a generic function to check if a variable contains a default value. It was easy to write one returning a default value: `T GetDefault() => (T)default;` But I couldn't write a function `IsDefault(value)` without restricting it to `IComparable` which didn't work for all types. With your answer it is now simple: `bool IsDefault(T value) => Compare((T)default, value);` is the answer to my question. Thanks, Jon! – Matt Nov 12 '20 at 09:32
  • Could you please update the answer with C#11 generic math option? (There is no way current answer with 2 votes will ever be found and adding that info by anyone else would be rejected) – Alexei Levenkov Mar 10 '23 at 18:06
  • @AlexeiLevenkov: No. I've upvoted Marc's answer though. – Jon Skeet Mar 10 '23 at 18:14
163

"...by default == behaves as described above for both predefined and user-defined reference types."

Type T is not necessarily a reference type, so the compiler can't make that assumption.

However, this will compile because it is more explicit:

    bool Compare<T>(T x, T y) where T : class
    {
        return x == y;
    }

Follow up to additional question, "But, in case I'm using a reference type, would the the == operator use the predefined reference comparison, or would it use the overloaded version of the operator if a type defined one?"

I would have thought that == on the Generics would use the overloaded version, but the following test demonstrates otherwise. Interesting... I'd love to know why! If someone knows please share.

namespace TestProject
{
 class Program
 {
    static void Main(string[] args)
    {
        Test a = new Test();
        Test b = new Test();

        Console.WriteLine("Inline:");
        bool x = a == b;
        Console.WriteLine("Generic:");
        Compare<Test>(a, b);

    }


    static bool Compare<T>(T x, T y) where T : class
    {
        return x == y;
    }
 }

 class Test
 {
    public static bool operator ==(Test a, Test b)
    {
        Console.WriteLine("Overloaded == called");
        return a.Equals(b);
    }

    public static bool operator !=(Test a, Test b)
    {
        Console.WriteLine("Overloaded != called");
        return a.Equals(b);
    }
  }
}

Output

Inline: Overloaded == called

Generic:

Press any key to continue . . .

Follow Up 2

I do want to point out that changing my compare method to

    static bool Compare<T>(T x, T y) where T : Test
    {
        return x == y;
    }

causes the overloaded == operator to be called. I guess without specifying the type (as a where), the compiler can't infer that it should use the overloaded operator... though I'd think that it would have enough information to make that decision even without specifying the type.

Glory Raj
  • 17,397
  • 27
  • 100
  • 203
Giovanni Galbo
  • 12,963
  • 13
  • 59
  • 78
  • 4
    Re: Follow Up 2: Actually the compiler will link it the best method it finds, which is in this case Test.op_Equal. But if you had a class that derives from Test and overrides the operator, then Test's operator will still be called. – Hosam Aly Dec 24 '08 at 13:28
  • 5
    I good practice that I would like to point out is that you should always do the actual comparison inside an overridden `Equals` method (not in the `==` operator). – jpbochi Aug 19 '09 at 16:34
  • 14
    Overload resolution happens compile-time. So when we have `==` between generic types `T` and `T`, the best overload is found, given what constraints are carried by `T` (there's a special rule that it will never box a value-type for this (which would give a meaningless result), hence there must be some constraint guaranteeing it's a reference type). In your _Follow Up 2_, if you come in with `DerivedTest` objects, and `DerivedTest` derives from `Test` but introduces a new overload of `==`, you will have the "problem" again. Which overload is called, is "burned" into the IL at compile-time. – Jeppe Stig Nielsen Jan 05 '13 at 18:51
  • 1
    wierdly this seems to work for general reference types (where you would expect this comparison to be on reference equality) but for strings it seems to also use reference equality - so you can end up comparing 2 identical strings and having == (when in a generic method with the class constraint) say they are different. – JonnyRaa May 14 '13 at 11:33
  • It's because the signature doesn't match -- your constraint is for `class`, which effectively means that, at compile time, these are both instances of type `object` -- your `==` operator requires that both operands be of type `Test`, so, that operator won't be called due to a signature mismatch (remember, method and operator signature matching happens at compile time!) -- Same thing would happen if you boxed an instance of the `Test` class, btw. (i.e. `Test a = new Test(); object b = new Test(); Console.WriteLine(a == b);` will always return `false`.) – BrainSlugs83 Jun 10 '18 at 02:08
50

In general, EqualityComparer<T>.Default.Equals should do the job with anything that implements IEquatable<T>, or that has a sensible Equals implementation.

If, however, == and Equals are implemented differently for some reason, then my work on generic operators should be useful; it supports the operator versions of (among others):

  • Equal(T value1, T value2)
  • NotEqual(T value1, T value2)
  • GreaterThan(T value1, T value2)
  • LessThan(T value1, T value2)
  • GreaterThanOrEqual(T value1, T value2)
  • LessThanOrEqual(T value1, T value2)
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • "If, however, == and Equals are implemented differently for some reason" - Holy smokes! What a however! Maybe I just need to see a use case to the contrary, but a library with divergent equals semantics will likely run into bigger problems than trouble with generics. – Edward Brey Jul 20 '17 at 16:53
  • @EdwardBrey you're not wrong; it would be nice if the compiler could enforce that, but... – Marc Gravell Jul 20 '17 at 17:56
47

So many answers, and not a single one explains the WHY? (which Giovanni explicitly asked)...

.NET generics do not act like C++ templates. In C++ templates, overload resolution occurs after the actual template parameters are known.

In .NET generics (including C#), overload resolution occurs without knowing the actual generic parameters. The only information the compiler can use to choose the function to call comes from type constraints on the generic parameters.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • 3
    but why can't compiler treat them as a generic object? after all `==` works for all types be it reference types or value types. That should be the question to which I dont think you answered. – nawfal Feb 03 '13 at 18:11
  • 5
    @nawfal: Actually no, `==` doesn't work for all value types. More importantly, it doesn't have the same meaning for all types, so the compiler doesn't know what to do with it. – Ben Voigt Feb 04 '13 at 00:17
  • 1
    Ben, oh yes I missed the custom structs we can create without any `==`. Can you include that part too in your answer as I guess that's the main point here – nawfal Feb 04 '13 at 00:20
  • I understand why this design decision was made. However, I'd argue that a better approach would be to try to call the most-derived `==` operator possible, and simply throw a MissingMethodException or something like that, if that situation is ever encountered, as it's probably an extreme minority, and is otherwise non-intuitive. – dodexahedron Jul 17 '23 at 03:36
  • Another potential solution at compile-time would be to make it a suppressable error, since, in a non-library project, the expected types are likely known, but might not be representable by a type filter (I have exactly this situation). So, I had to implement closed generic interfaces for all known `T`s, which was a lot of code duplication, and nearly eliminates the utility of the generic in the first place. Plus, all the duplicated code is _identical_, but mandatory, because the `==` operator cannot take a type parameter. – dodexahedron Jul 17 '23 at 03:53
  • @dodexahedron: No, it can't be suppressed, because the compiler fails to find any function to call. It *can't* compile the code. – Ben Voigt Jul 17 '23 at 14:26
  • @BenVoigt The actual problem with what I suggested is that it throws away the generic behavior. What would be more useful, I think, is for type parameter filters to have a way to specify an OR list of possible types, rather than only the AND, as it currently is. So many of these would be solved by being able to use a filter like `where T : not null | struct` or something, since the struct or not struct limitation is what makes this such a problem so often. Then the fallback would be ValueType.Equals, which would be compilable and throw an exception at run time if mismatched, wouldn't it? – dodexahedron Jul 18 '23 at 23:43
  • @BenVoigt and, so long as the struct paths are tested first, boxing would still be avoided, so long as an `==` exists with both the left and right types. But that's bordering on multiple inheritance, which C# just doesn't really do. However, I don't think that isolated application of not-really-multiple-inheritance would require significant architectural changes, since the same _effect_ is achieved with pattern matching on types, after boxing. This would just be a way of avoiding the boxing. – dodexahedron Jul 18 '23 at 23:48
  • @dodexahedron: If you want `ValueType.Equals` you can just call it yourself. But the `==` operator never maps automatically to `ValueType.Equals`, so it would be extremely weird for it to do so only in generic context. – Ben Voigt Jul 19 '23 at 14:22
  • @BenVoigt Do I misunderstand this document, then? https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1815 \ By my reading, anyway, a non-blittable struct already does do that, by reflection, for the compiler-generated equality, no? Blittable structs are just by each field value, so they of course don't take that path. But, clearly, the code path does exist, or else this warning wouldn't exist. I've run into that stack overflow situation when a `record struct` type in a project became non-blittable after a change, which was non-obvious to track down. – dodexahedron Jul 19 '23 at 23:22
  • @dodexahedron: You misunderstand. That page doesn't say there is a compiler-generated `operator==`. It describes the behavior of passing instances of a `struct` to `Object.Equals()`. – Ben Voigt Jul 20 '23 at 14:18
  • Just adding on here in case someone stumbles onto the conversation. It is correct to say that, by default, a regular user-defined `struct` does not implement `operator==`, and you must overload it yourself to get it for a `struct`. However, `record struct` types do implement it and you cannot override it. That implementation does use ValueType.Equals, and achieves it by reflection (yikes!). That was the source of the confusion. `record struct`s behave differently out of the box, in that way. – dodexahedron Aug 18 '23 at 15:46
  • It's specifically called out here: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/record#value-equality. Specifically, each member of the `record struct` is compared. If they are value types, the generated code will use ValueType.Equals – dodexahedron Aug 18 '23 at 15:48
  • Gah. I keep taking too long to add my thoughts... It makes sense that the generated `operator==` for record structs calls ValueType.Equals on value type members, since those members are not guaranteed to have an `operator==`, but WILL have at least an implicit ValueType.Equals they inherit from ValueType. – dodexahedron Aug 18 '23 at 15:55
  • Many structs are not designed to work with ValueType.Equals. Your proposal creates a pit of failure (everything compiles but then it fails to invoke the correct comparison). My first comment, which observed that `==` doesn't have the same meaning for all types, is just as valid today as ever. – Ben Voigt Aug 18 '23 at 15:58
  • Exactly why it was a problem. But that's the way they behave. So says the spec, and it jives with the exceptions I received with a generic `record struct`. It seems like a rather unfortunate decision that the operator can't be overridden for a record struct. One can override IEquatable, but, as I found out, implementing the open generic for that results in it never being called, since the compiler resolves it at compile-time, and object is a better match than T. That's why, to get the desired behavior and avoid boxing, one has to implement closed generics, so an exact type is resolved. :( – dodexahedron Aug 18 '23 at 16:02
  • It's not a proposal. It's just the way a `record struct` works. ‍♂️ Armed with that knowledge, it is actually quite easy to replicate the same exception, which shows a stack trace including ValueType.Equals being invoked by the compiler-generated code. Again, this is for `record struct` types only. Normal structs don't do this unless you explicitly call .Equals on them. – dodexahedron Aug 18 '23 at 16:03
  • You're 100% correct on the behavior of a normal struct, with it never being an implicit thing...until you make that struct a member of a `record struct`. Then that assumption gets tossed out the window, which is not obvious without having either encountered the problem or read the doc on that behavior. I dislike it, because it was _different_ than how a `struct` behaves in every situation _except_ that one. – dodexahedron Aug 18 '23 at 16:10
  • Ah, I think you were probably referring to my earlier comment from last month, yes? Personally, I prefer a solution that has an obvious failure mode. The inconsistency created by the behavior of record structs can be resolved multiple different ways. Since structs already get an implicit ValueType.Equals that compares each member by reflection, what would be the harm in also having a default `operator==` that simply calls that method? It's of course only one of many ways to do it, but it seems intuitive, at least to me. – dodexahedron Aug 18 '23 at 16:22
  • Actually I was referring to [this one in 2013](https://stackoverflow.com/questions/390900/cant-operator-be-applied-to-generic-types-in-c/4423902?noredirect=1#comment20521305_4423902). And one of the leading theories of language design is "fail early". So no "solution" with common failure modes, just refuse to accept the code that's likely to fail. – Ben Voigt Aug 18 '23 at 17:55
17

The compile can't know T couldn't be a struct (value type). So you have to tell it it can only be of reference type i think:

bool Compare<T>(T x, T y) where T : class { return x == y; }

It's because if T could be a value type, there could be cases where x == y would be ill formed - in cases when a type doesn't have an operator == defined. The same will happen for this which is more obvious:

void CallFoo<T>(T x) { x.foo(); }

That fails too, because you could pass a type T that wouldn't have a function foo. C# forces you to make sure all possible types always have a function foo. That's done by the where clause.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • 1
    Hosam, i tested with gmcs (mono), and it always compares references. (i.e it doesn't use an optionally defined operator== for T) – Johannes Schaub - litb Dec 24 '08 at 08:43
  • There is one caveat with this solution: the operator== cannot be overloaded; [see this StackOverflow question](http://stackoverflow.com/questions/2919232/using-overloaded-operator-in-a-generic-function). – Dimitri C. May 27 '10 at 10:00
  • @DimitriC.: You totally misrepresented that other question (even though you're the one who wrote it). It doesn't show that operator== cannot be overloaded (in fact it can). It shows only that generic functions can't find operator== overloads. – Ben Voigt Mar 23 '23 at 21:32
12

It appears that without the class constraint:

bool Compare<T> (T x, T y) where T: class
{
    return x == y;
}

One should realize that while class constrained Equals in the == operator inherits from Object.Equals, while that of a struct overrides ValueType.Equals.

Note that:

bool Compare<T> (T x, T y) where T: struct
{
    return x == y;
}

also gives out the same compiler error.

As yet I do not understand why having a value type equality operator comparison is rejected by the compiler. I do know for a fact though, that this works:

bool Compare<T> (T x, T y)
{
    return x.Equals(y);
}
Jon Limjap
  • 94,284
  • 15
  • 101
  • 152
  • u know im a total c# noob. but i think it fails because the compiler doesn't know what to do. since T isn't known yet, what is done depends on the type T if value types would be allowed. for references, the references are just compared regardless of T. if you do .Equals, then .Equal is just called. – Johannes Schaub - litb Dec 24 '08 at 07:02
  • 1
    but if you do == on a value type, the value type doesn't have to necassary implement that operator. – Johannes Schaub - litb Dec 24 '08 at 07:05
  • That'd make sense, litb :) It is possible that user-defined structs do not overload ==, hence the compiler fail. – Jon Limjap Dec 24 '08 at 07:37
  • 2
    The first compare method does *not* use `Object.Equals` but instead tests reference equality. For example, `Compare("0", 0.ToString())` would return false, since the arguments would be references to distinct strings, both of which have a zero as their only character. – supercat May 30 '13 at 21:27
  • 1
    Minor gotcha on that last one- you haven't restricted it to structs, so a `NullReferenceException` could happen. – Flynn1179 Apr 20 '17 at 14:13
7

Well in my case I wanted to unit-test the equality operator. I needed call the code under the equality operators without explicitly setting the generic type. Advises for EqualityComparer were not helpful as EqualityComparer called Equals method but not the equality operator.

Here is how I've got this working with generic types by building a LINQ. It calls the right code for == and != operators:

/// <summary>
/// Gets the result of "a == b"
/// </summary>
public bool GetEqualityOperatorResult<T>(T a, T b)
{
    // declare the parameters
    var paramA = Expression.Parameter(typeof(T), nameof(a));
    var paramB = Expression.Parameter(typeof(T), nameof(b));
    // get equality expression for the parameters
    var body = Expression.Equal(paramA, paramB);
    // compile it
    var invokeEqualityOperator = Expression.Lambda<Func<T, T, bool>>(body, paramA, paramB).Compile();
    // call it
    return invokeEqualityOperator(a, b);
}

/// <summary>
/// Gets the result of "a =! b"
/// </summary>
public bool GetInequalityOperatorResult<T>(T a, T b)
{
    // declare the parameters
    var paramA = Expression.Parameter(typeof(T), nameof(a));
    var paramB = Expression.Parameter(typeof(T), nameof(b));
    // get equality expression for the parameters
    var body = Expression.NotEqual(paramA, paramB);
    // compile it
    var invokeInequalityOperator = Expression.Lambda<Func<T, T, bool>>(body, paramA, paramB).Compile();
    // call it
    return invokeInequalityOperator(a, b);
}
U. Bulle
  • 1,075
  • 9
  • 20
  • It's bad form to have Equals() not behave the same as `==`. Aside from that, though, this seems like a run-time expensive solution. If the expected set of types for `T` is not large, but a type parameter filter isn't possible (such as when `T` can be value _and_ reference types), explicitly implementing the closed generic interfaces, while tedious, results in better runtime performance, more intuitive behavior, and sometimes even a smaller compiled assembly than the open generic (always, in my experience, but I'm not going to assert a definite here). – dodexahedron Jul 17 '23 at 03:58
  • I had considered a solution almost exactly like this when trying to solve this problem, but bit the bullet and implemented IComparable for every type the application actually accepts/uses, and it's mostly just a copy/paste job, since the equality/inequality operators are identical except for the types in their signatures. It feels dirty, but, if the expected set of types is known, it's perfectly valid. I would love to have an alternative, though, such as type filters as a list, or even just generics resolving at runtime and throwing an exception at runtime if equality isn't defined. – dodexahedron Jul 17 '23 at 04:03
6

There is an MSDN Connect entry for this here

Alex Turner's reply starts with:

Unfortunately, this behavior is by design and there is not an easy solution to enable use of == with type parameters that may contain value types.

Recep
  • 18,991
  • 2
  • 28
  • 21
5

If you want to make sure the operators of your custom type are called you can do so via reflection. Just get the type using your generic parameter and retrieve the MethodInfo for the desired operator (e.g. op_Equality, op_Inequality, op_LessThan...).

var methodInfo = typeof (T).GetMethod("op_Equality", 
                             BindingFlags.Static | BindingFlags.Public);    

Then execute the operator using the MethodInfo's Invoke method and pass in the objects as the parameters.

var result = (bool) methodInfo.Invoke(null, new object[] { object1, object2});

This will invoke your overloaded operator and not the one defined by the constraints applied on the generic parameter. Might not be practical, but could come in handy for unit testing your operators when using a generic base class that contains a couple of tests.

Christophe
  • 51
  • 1
  • 2
4

I wrote the following function looking at the latest msdn. It can easily compare two objects x and y:

static bool IsLessThan(T x, T y) 
{
    return ((IComparable)(x)).CompareTo(y) <= 0;
}
Leandro Caniglia
  • 14,495
  • 4
  • 29
  • 51
Charlie
  • 4,827
  • 2
  • 31
  • 55
  • 4
    You can get rid of your booleans and write `return ((IComparable)(x)).CompareTo(y) <= 0;` – aloisdg Jun 26 '16 at 18:23
  • 1
    What if `T` isn't an `IComparable`? Won't this throw an invalid cast exception? And if `T` is an `IComparable` why not just add that to the generic type constraint? – MetaFight Sep 06 '21 at 13:00
  • @MetaFight IComparable is an interface. – Charlie Sep 10 '21 at 19:49
  • 1
    @Charlie yes, I'm aware. My concern is that nothing guarantees `T` actually implements that interface. If it doesn't, then you get an exception. If it does, then it should probably be part of the generic constraint. – MetaFight Sep 11 '21 at 23:41
4

You can do this with C# 11 and .NET 7+:

    static void Main()
    {
        Console.WriteLine(Compare(2, 2));
        Console.WriteLine(Compare(2, 3));
    }
    static bool Compare<T>(T x, T y) where T : IEqualityOperators<T, T, bool>
    {
        return x == y;
    }

(you may prefer to use where T : INumber<T>, which covers this scenario and a lot more, but it depends on your specific needs; not all equatable types are numbers)

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Thanks! This is the [Generic math support](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#generic-math-support) feature in C# 11, isn't it? Does it work with .NET 6? – Hosam Aly Dec 20 '22 at 15:14
  • 1
    @HosamAly yes, but it relies upon implementation in the actual primitive types, which .net 6 lacks; it'll work for your own types and interfaces, but not for the inbuilt types, which really means "no it won't work" – Marc Gravell Dec 22 '22 at 09:35
  • Very clear. Thanks, @Marc! – Hosam Aly Dec 23 '22 at 20:13
  • What if your generic Type T is not an INumber? How would this work? Given its generic and hasn't been constrained otherwise, we don't know what kind of type it is. How would this work for e.g. string, or user-defined reference class POCO types? – nam20485 Aug 12 '23 at 15:30
3

bool Compare(T x, T y) where T : class { return x == y; }

The above will work because == is taken care of in case of user-defined reference types.
In case of value types, == can be overridden. In which case, "!=" should also be defined.

I think that could be the reason, it disallows generic comparison using "==".

shahkalpesh
  • 33,172
  • 3
  • 63
  • 88
  • 1
    The `==` token is used for two different operators. If for the given operand types there exists a compatible overload of the equality operator, that overload will be used. Otherwise if both operands are reference types that are compatible with each other, a reference comparison will be used. Note that in the `Compare` method above the compiler can't tell that the first meaning applies, but can tell the second meaning applies, so the `==` token will use the latter *even if `T` overloads the equality-check operator (e.g. if it's type `String`)*. – supercat May 30 '13 at 21:24
1

The .Equals() works for me while TKey is a generic type.

public virtual TOutputDto GetOne(TKey id)
{
    var entity =
        _unitOfWork.BaseRepository
            .FindByCondition(x => 
                !x.IsDelete && 
                x.Id.Equals(id))
            .SingleOrDefault();


    // ...
}
Masoud Darvishian
  • 3,754
  • 4
  • 33
  • 41
  • 1
    That's `x.Id.Equals`, not `id.Equals`. Presumably, the compiler knows something about the type of `x`. – Hosam Aly Oct 19 '19 at 07:39
1

I have 2 solutions and they're very simply.

Solution 1: Cast the generic typed variable to object and use == operator.

Example:

void Foo<T>(T t1, T t2)
{
   object o1 = t1;
   object o2 = t2;
   if (o1 == o2)
   {
      // ...
      // ..
      // . 
   }
}

Solution 2: Use object.Equals(object, object) method.

Example:

void Foo<T>(T t1, T t2)
{
   if (object.Equals(t1, t2)
   {
       // ...
       // ..
       // .
   }
}
1 JustOnly 1
  • 171
  • 11