8

I just ran into this situation and i thought it was a nice opportunity to use the default keyword. But it doesn't compile and i can't think of why. The example below illustrates my problem:

public class Test<TDataSource>
{
    public IQueryable<TDataSource> DataSource { get; set; }

    public bool GetOneOrDefaultResult()
    {
        var result = DataSource.SingleOrDefault();
        return result != default(TDataSource);
    }
}

You'll get an error on line 8 ("Operator '==' cannot be applied to operands of type 'TDataSource' and 'TDataSource'."). I thought using the default keyword would eliminate any comparison problems between reference types and value types.

Adding a generic constraint limiting TDataSource to reference types makes this piece of code compile.

Can somebody explain why the compiler won't fix this for me? Is it just not smart enough to see this would work?

This is related: Can't operator == be applied to generic types in C#?

[Edit] SLaks answer gave me some inspiration, the '==' operator won't work, but the Equals function should.

    public class Test<TDataSource>
{
    public IQueryable<TDataSource> DataSource { get; set; }

    public bool GetOneOrDefaultResult()
    {
        var result = DataSource.SingleOrDefault();
        return result.Equals(default(TDataSource));
    }
}

This compiles would this function correctly?

Community
  • 1
  • 1
JJoos
  • 587
  • 1
  • 6
  • 17
  • If you're just trying to figure out if there was a result, you're doing it wrong because you won't be able to tell the difference between no result and the result being 0. – Gabe Sep 03 '10 at 13:44
  • Gabe the logic of the function is only an example, it's not useful. – JJoos Sep 03 '10 at 13:48
  • 1
    Your edit can throw a NullReferenceExeption. You need to call the static `Equals(a, b)` method. – SLaks Sep 03 '10 at 22:35
  • possible duplicate of [Null or default comparison of generic argument in C#](http://stackoverflow.com/questions/65351/null-or-default-comparison-of-generic-argument-in-c-sharp) – Andrej Adamenko Sep 24 '14 at 13:54

1 Answers1

6

You cannot assume that every value type overrides the == operator. (And even if they did, there would be no way to call it using generics; it's a static method)

Instead, you should write

    return !(ReferenceEquals((object)result, (object)default(TDataSource)) 
          || result.Equals(default(TDataSource)));

If result is null (and a reference type), the ReferenceEquals call will return true, so Equals won't be called and won't throw a NullReferenceException.
If TDataSource is a value type, the ReferenceEquals will compare two different boxed references (which may happen to contain the same value, but will still be different), so it will pass on to the Equals call.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • Note that in general, this is how you have to compare two values of unknown type for equality. (i.e. this doesn't apply only to looking for default values) – Gabe Sep 03 '10 at 13:45
  • You're absolutely right. Are this only user defined structs that don't override the '==' operator? – JJoos Sep 03 '10 at 13:46
  • @JJoos, if value types override the `==` operator, it's considered best practice to override `Equals()` so that it behaves equivalently. This is not strictly guaranteed, but I would consider it a bug if I found a value type that did not behave in this way. – Dan Bryant Sep 03 '10 at 13:55
  • 4
    No need for something so complicated, you just need to use the static Object.Equals method : `Equals(result, default(TDataSource))`. – Thomas Levesque Sep 03 '10 at 14:04
  • 1
    @Thomas Levesque, Yeah also thought of that after @SLaks post. Seems to work correctly and is a lot more readable. – JJoos Sep 03 '10 at 14:44
  • This may also be relevant: http://stackoverflow.com/questions/65351/null-or-default-comparison-of-generic-argument-in-c-sharp – BgRva Jul 15 '14 at 12:49