Return a new instance rather than a null instance in generics

deanvmc picture deanvmc · Jul 16, 2011 · Viewed 10k times · Source

I have a class which pulls objects from Isolated Storage. If it cannot find the object in question it returns default(T) which would be null as they are reference types. If the value returned is null I do a simple check and assign a new instance in the caller but I would prefer to do this in the storage logic.

So my question, is there a way to return a new T where the object has a default blank constructor?

Answer

Theraot picture Theraot · Jul 16, 2011

An option would be use the contraint "new": http://msdn.microsoft.com/en-us/library/sd2w2ew5(v=vs.80).aspx

Like so:

public T GetNewItem()
    where T: new()
{
    return new T();
}

But having this constraint means that you can't use a type that doesn't have a default constructor. So you may consider to use System.Activator.CreateInstance, but remember that it may throw an exception:

T createInstance<T>()
{
    try
    {
        return System.Activator.CreateInstance<T>();
    }
    catch (MissingMethodException exc)
    {
        return default(T);
    }
}

So, it may be a good idea to know if the given type supports this early in the initialization, a way to do so follows:

T createInstance<T>()
{
    System.Reflection.ConstructorInfo constructor = (typeof(T)).GetConstructor(System.Type.EmptyTypes);
    if (ReferenceEquals(constructor, null))
    {
        //there is no default constructor
        return default(T);
    }
    else
    {
        //there is a default constructor
        //you can invoke it like so:
        return (T)constructor.Invoke(new object[0]);
        //return constructor.Invoke(new object[0]) as T; //If T is class
    }
}

While you are at it, why not get a delegate that creates an instance?

Func<T> getConstructor<T>()
{
    System.Reflection.ConstructorInfo constructor = (typeof(T)).GetConstructor(System.Type.EmptyTypes);
    if (ReferenceEquals(constructor, null))
    {
        return () => { return default(T); };
    }
    else
    {
        return () => { return (T)constructor.Invoke(new object[0]); };
    }
}

An example of how to use it (compiled with LinqPad):

void Main()
{
    Console.WriteLine(getConstructor<object>()());
    Console.WriteLine(getConstructor<int>()());
    Console.WriteLine(getConstructor<string>()());
    Console.WriteLine(getConstructor<decimal>()());
    Console.WriteLine(getConstructor<DateTime>()());
    Console.WriteLine(getConstructor<int?>()());
}

The output is:

System.Object
0
null
0
01/01/0001 12:00:00 a.m.
null

The case of string is a special case, being a referece type it can be null, and not having a public default constructor that's what you get here instead of String.Empty. The nullable type also gives null.