tags:

views:

935

answers:

7

I need to check a generic object for null, or default(T). But I have a problem... Currently I have done it like this:

if (typeof(T).IsValueType)
{
  if(default(T).Equals(thing))
    // Do something
  else
    // Do something else
}
else
{
  if(thing == null)
    // Do something
  else
    // Do something else
}

But then I end up repeating myself... which I don't like. The problem is the following:

thing == null;

Here ReSharper warns about Possible compare of value type with 'null'.

thing == default(T);

Here I get compiler error: Cannot apply operator '==' to operands of type 'T' and 'T'.

thing.Equals(null|default(T));

thing can obviously be null (that's why I have to check!), so will cause NullReferenceException.

null|default(T).Equals(thing);

null and default(T) is very often null as well...

Is there a clean way to do this??

+2  A: 

Best thing I can think of at the moment is:

return value == null || value.Equals(default(T));

Edit:

Apparently, there's a static object.Equals method I was not aware of:

return object.Equals(value, default(T));

This is better.

configurator
but what will then happen if value is a value type?
Svish
(value == null) would return false. (value.Equals(default(T)) would check against the default.
configurator
This only avoids boxing if there is an Equals implementation on the type that is specialized rather than the default that takes an object. In light of that, I think that object.Equals(value, default(T)) is easier to read.
Jeff Yates
@Jeff: I agree. Like I said, object.Equals() is a method I was not aware of.
configurator
@Jeff: The Equals implementation must override object.Equals in order to be called. So this never avoids boxing.
configurator
You can avoid boxing by implementing IEquatable.
Jeff Yates
But of course, you need to query for that before resorting to object.Equals.
Jeff Yates
+2  A: 

A bit of boxing will do the job just fine.

    static bool IsNullOrDefault<T>(T value)
    {
        return ((object)default(T)) == null ?
            ((object)value) == null :
            default(T).Equals(value);
    }
Anton Gogolev
Isn't this just saying object.Equals(default(T), value)?
Jeff Yates
A: 

With Tests:

public class DefaultOrNullChecker<T>  {
    public bool Check(object x) {
        return object.ReferenceEquals(x, null) || x.Equals(default(T));
    }
}
[TestFixture]
public class Tests {
    [Test]  public void when_T_is_reference_type() { 
        Assert.IsFalse(new DefaultOrNullChecker<Exception>().Check(new Exception()));}
    [Test] public void when_T_is_value_type() { 
        Assert.IsFalse(new DefaultOrNullChecker<int>().Check(123)); }
    [Test] public void when_T_is_null() {
        Assert.IsTrue(new DefaultOrNullChecker<Exception>().Check(null));}
    [Test] public void when_T_is_default_value() { 
        Assert.IsTrue(new DefaultOrNullChecker<int>().Check(0)); }
}
George Mauer
This does not matter at all since the comparison is done on T's constraint vs T's constraint, i.e. object == object. That is the same as object.ReferenceEquals.
configurator
+14  A: 

If boxing isn't an issue, you could just use:

object.Equals(value, default(T))
Jeff Yates
I didn't know that was possible.
configurator
and that will work if both are null?
Svish
It does. Checking isn't that hard, Svish...
configurator
I'm surprised at how complex the other answers are for this.
Jeff Yates
How hard checking is, depends where you are. Thank you Jeff, will most likely end up with this method =)
Svish
See Jason's answer for a one-liner that doesn't need boxing.
Matt Howells
A: 

You can avoid boxing altogether by noting that nullability of a type can be determined statically.

Here is what you can do:

  1. Declare a private static read-only variable called isDefault of type Predicate<T> in your generic class
  2. Add a static initializer to your generic class, where you check T's nullability, and set isDefault to either v==null or default(T).Equals(v) depending on the outcome
  3. Use isDefault(x) instead of x==null in the rest of your code

Here is an example:

public class Example<T> {

    private static readonly Predicate<T> isDefault;

    static Example() {
        // Nullability check is a bit ugly, but we do it once per T,
        // so what the heck...
        if (typeof(T).IsValueType &&
           (!typeof(T).IsGenericType
        ||  typeof(T).GetGenericTypeDefinition() != typeof(Nullable<>)
        )) {
            // This is safe because T is not null
            isDefault = val => default(T).Equals(val);
        } else {
            // T is not a value type, so its default is null
            isDefault = val => val == null;
        }
    }

    public bool Check(T value) {
        // Now our null-checking is both good-looking and efficient
        return isDefault(value);
    }

}
Sergey
What if the method is generic but the class is not?
Matt Howells
+10  A: 

The proper way to do this is:

return EqualityComparer<T>.Default.Equals(value, default(T))

No boxing. You could even define an extension method like this:

public static void bool IsDefault<T>(this T value)
{
    return EqualityComparer<T>.Default.Equals(value, default(T));
}

.. and invoke it like this:

return entry.IsDefault();

Though, I personally don't care for extension methods on T (e.g. this object IsNull()) since it hampers readability sometimes.

Jason
Oh, nice one :)
Svish
A: 

What is wrong with this?

if (thing == default(T)) { }

If it is a value type then the JIT will simply remove the statement altogether.

Andrew Hare
It doesn't compile. `Cannot apply operator '==' to operands of type 'T' and 'T'`
Matt Howells