152

This code snippet works as expected for the int type:

public class Test 
{
    public int Value
    {
        get => _Value;
        set
        {
            if (_Value != value)
                _Value = value;
        }
    }
    private int _Value;
}

When int is replaced by the generic T, the compiler complains with:

Operator '!=' cannot be applied to operands of type 'T' and 'T'

Why does this happen and is there a way to solve it?

AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
Darf
  • 2,495
  • 5
  • 26
  • 37

3 Answers3

209
using System.Collections.Generic;

public class Test<T>
{
    public T Value
    {
         get => _Value; 
         set
         {
            // operator== is undefined for generic T; EqualityComparer solves this
            if (!EqualityComparer<T>.Default.Equals(_Value, value))
            {
                _Value = value;
            }
         }
    }
    private T _Value;
}
AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
user541686
  • 205,094
  • 128
  • 528
  • 886
  • The year isn't that surprising, but that none of the 18 upvoters noticed is. – CodesInChaos Oct 21 '13 at 21:11
  • 10
    @Mehrdad, I would use `public class Test where T : IEquatable` and then call `_Value.Equals(value)`, which would be semantically stricter, as it is no longer an implementation detail but a class declaration. – Herman Kan Nov 24 '15 at 07:06
  • @HermanKan: And what if `IEquatable` isn't implemented? Your would fail, but this would use the default comparator. – user541686 Nov 24 '15 at 07:40
  • 1
    @Mehrdad, the failure would be at compile time which is just my purpose. Since the equality check is integral part of the setter, one would need the value to be capable of being equated anyway. Most value types have the `IEquatable` interface implemented. – Herman Kan Nov 24 '15 at 09:29
  • 1
    @HermanKan: The point is that the solution needs to work for types that don't implement `IEquatable`, like `object`. – user541686 Nov 24 '15 at 10:32
  • 5
    Can you add some explanation as to why this code solves the problem? – Tot Zam Oct 14 '16 at 15:41
  • 1
    @TotZam: I don't really understand the question. The problem was that `operator==` was undefined for generic `T` and `T`, and this solves the problem by entirely avoiding `operator==`. Is that unclear in the code or do you mean something else? – user541686 Aug 12 '17 at 09:46
  • 4
    @Mehrdad I added the above comment a while ago, but I am assuming that I did not understand how your code solved the problem by just looking at the code. To improve this answer, I would add a sentence to the answer explaining what you just mentioned, that you can work around the problem by avoiding the `==` operator. – Tot Zam Aug 13 '17 at 02:50
121

T is a type argument and can be a class or a struct, Thus the compiler won't let you perform actions that don't exist both in classes and structs.

structs don't have the == and != by default(but can be added), this is why the compiler complains.

If you use the where keyword to add a constraint to the type argument, the compiler will let you use that type\interface method\operators

constrain T to be a class

public class Test<T> where T : class
{
     public T Value
     {
         private T _Value;
         
         get { return _Value; }
         set
         {
             if (_value != value)
                 _Value = value;             
         }
     }
}

Or simply use Equals instead of the == operator

public class Test<T>
{
     public T Value
     {
         private T _Value;
         
         get { return _Value; }
         set
         {
             if (!_value.Equals(value)
                 _Value = value;             
         }
     }
}
gdoron
  • 147,333
  • 58
  • 291
  • 367
  • 3
    In your second example, if _value is null, you will throw a `NullReferenceException`. Best to use Mehrdad's example where you need to support structs. – Sean Lynch Aug 15 '13 at 14:55
  • 1
    @SeanLynch, you are absolutely right, but it's just an example, it's need more work than simply copy & paste. – gdoron Aug 15 '13 at 17:14
  • My types are most often `int` and `string`. If I remember well, I can't constrain my generic type to those, is that right? In this case, am I forced to use `Equals`? – Marc.2377 Jan 27 '21 at 19:37
28

T can be any type. You cannot use ==/!= on structs, unless such operators are defined on the (struct) type.

leppie
  • 115,091
  • 17
  • 196
  • 297