tags:

views:

967

answers:

4
void Get<T>(Action<T> createObject)
{
    T obj = createObject();
    if(obj == default(T))
        return obj;

    // .. do a bunch of stuff
    return obj;
}

Compiler error: Operator '==' cannot be applied to operands of type 'T' and 'T'

How do I do this properly?

A: 

(Edited)

Marc Gravell has the best answer, but I wanted to post a simple code snippet I worked up to demonstrate it. Just run this in a simple C# console app:

public static class TypeHelper<T>
{
    public static bool IsDefault(T val)
    {
         return EqualityComparer<T>.Default.Equals(obj,default(T));
    }
}

static void Main(string[] args)
{
    // value type
    Console.WriteLine(TypeHelper<int>.IsDefault(1)); //False
    Console.WriteLine(TypeHelper<int>.IsDefault(0)); // True

    // reference type
    Console.WriteLine(TypeHelper<string>.IsDefault("test")); //False
    Console.WriteLine(TypeHelper<string>.IsDefault(null)); //True //True

    Console.ReadKey();
}

One more thing: can someone with VS2008 try this as an extension method? I'm stuck with 2005 here and I'm curious to see if that would be allowed.


Edit: Here is how to get it working as an extension method:

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
     // value type
     Console.WriteLine(1.IsDefault());
     Console.WriteLine(0.IsDefault());

     // reference type
     Console.WriteLine("test".IsDefault());
     // null must be cast to a type
     Console.WriteLine(((String)null).IsDefault());
    }
}

// The type cannot be generic
public static class TypeHelper
{
    // I made the method generic instead
    public static bool IsDefault<T>(this T val)
    {
     return EqualityComparer<T>.Default.Equals(val, default(T));
    }
}
Joel Coehoorn
obj might be a null value...
Nathan Ridley
Do a check for null first?
Davy8
It does "work" as an extension method. Which is interesting since it works even if you say o.IsDefault<object>() when o is null. Scary =)
Nick Farina
+5  A: 

To handle all types of T, including where T is a primitive type, you'll need to compile in both methods of comparison:

    T Get<T>(Func<T> createObject)
    {
        T obj = createObject();
        if (obj == null || obj.Equals(default(T)))
            return obj;

        // .. do a bunch of stuff
        return obj;
    }

EDIT: Marc's answer (below) is more correct: avoids boxing entirely when possible and respects custom equality implementations to boot.

Nick Farina
Note that the function has been changed to accept Func<T> and return T, which I think was accidentally omitted from the questioner's code.
Nick Farina
Seems ReSharper is messing with me. Didn't realise its warning about a possible comparison between a value type and null was not a compiler warning.
Nathan Ridley
FYI: If T turns out to be a value type then the comparison against null will be treated as always false by the jitter.
Eric Lippert
Makes sense - the runtime will be comparing a pointer to a value type. The Equals() check does work in that case however (interestingly, since it seems very dynamic-language to say 5.Equals(4) which does compile).
Nick Farina
See the EqualityComparer<T> answer for an alternative that doesn't involve boxing et
Marc Gravell
A: 

There is going to be a problem here -

If you're going to allow this to work for any type, default(T) will always be null for reference types, and 0 (or struct full of 0) for value types.

This is probably not the behavior you're after, though. If you want this to work in a generic way, you probably need to use reflection to check the type of T, and handle value types different than reference types.

Alternatively, you could put an interface constraint on this, and the interface could provide a way to check against the default of the class/struct.

Reed Copsey
+17  A: 

To avoid boxing, the best way to compare generics for equality is with EqualityComparer<T>.Default. This respects IEquatable<T> (without boxing) as well as object.Equals, and handles all the Nullable<T> "lifted" nuances. Hence:

if(EqualityComparer<T>.Default.Equals(obj,default(T)) {
    return obj;
}

This will match:

  • null for classes
  • null (empty) for Nullable<T>
  • zero/false/etc for other structs
Marc Gravell
Wow, how delightfully obscure! This is definitely the way to go though, kudos.
Nick Farina
Definitely the best answer. No squiggly lines in my code after rewriting to use this solution.
Nathan Ridley
Great answer! Even better is adding an extension method for this line of code so that you can go obj.IsDefaultForType()
Riko