I'd like to have generic method which returns default value for passed type, but for collection type I'd like to get empty collection instead of null, for example:
GetDefault<int[]>(); // returns empty array of int's
GetDefault<int>(); // returns 0
GetDefault<object>(); // returns null
GetDefault<IList<object>>(); // returns empty list of objects
The method I started to write is following:
public static T GetDefault<T>()
{
var type = typeof(T);
if(type.GetInterface("IEnumerable") != null))
{
//return empty collection
}
return default(T);
}
How to complete it ?
EDIT: If anyone would like get default value of some type, based on type instance instead of type identifier, this construction below can be used, i.e.:
typeof(int[]).GetDefault();
The implementation is internally based on @280Z28 answer:
public static class TypeExtensions
{
public static object GetDefault(this Type t)
{
var type = typeof(Default<>).MakeGenericType(t);
var property = type.GetProperty("Value", BindingFlags.Static | BindingFlags.Public);
var getaccessor = property.GetGetMethod();
return getaccessor.Invoke(null, null);
}
}
You can use the magic of a static constructor to do this efficiently. To use the default value in code, simply use Default<T>.Value
. The value will only be evaluated for any given type T
once for the duration of your application.
public static class Default<T>
{
private static readonly T _value;
static Default()
{
if (typeof(T).IsArray)
{
if (typeof(T).GetArrayRank() > 1)
_value = (T)(object)Array.CreateInstance(typeof(T).GetElementType(), new int[typeof(T).GetArrayRank()]);
else
_value = (T)(object)Array.CreateInstance(typeof(T).GetElementType(), 0);
return;
}
if (typeof(T) == typeof(string))
{
// string is IEnumerable<char>, but don't want to treat it like a collection
_value = default(T);
return;
}
if (typeof(IEnumerable).IsAssignableFrom(typeof(T)))
{
// check if an empty array is an instance of T
if (typeof(T).IsAssignableFrom(typeof(object[])))
{
_value = (T)(object)new object[0];
return;
}
if (typeof(T).IsGenericType && typeof(T).GetGenericArguments().Length == 1)
{
Type elementType = typeof(T).GetGenericArguments()[0];
if (typeof(T).IsAssignableFrom(elementType.MakeArrayType()))
{
_value = (T)(object)Array.CreateInstance(elementType, 0);
return;
}
}
throw new NotImplementedException("No default value is implemented for type " + typeof(T).FullName);
}
_value = default(T);
}
public static T Value
{
get
{
return _value;
}
}
}
IList<object>
is not a collection type, it's an interface. There are dozens of possible classes you could return.
If you pass in an actual collection type, you can do this:
public static T GetDefault<T>() where T : new
{
if (typeof(IEnumerable).IsAssignableFrom(typeof(T)))
{
return new T();
}
return default(T);
}
GetDefault<List<object>>();
To handle both empty collections, and null
values of types without default constructors, you can do it like this:
public static T GetDefault<T>()
{
if (typeof(IEnumerable).IsAssignableFrom(typeof(T)))
{
if (typeof(T).IsGenericType)
{
Type T_template = typeof(T).GetGenericTypeDefinition();
if (T_template == typeof(IEnumerable<>))
{
return (T)Activator.CreateInstance(typeof(Enumerable).MakeGenericType(typeof(T).GetGenericArguments()));
}
if (T_template == typeof(IList<>))
{
return (T)Activator.CreateInstance(typeof(List<>).MakeGenericType(typeof(T).GetGenericArguments()));
}
}
try {
return Activator.CreateInstance<T>();
}
catch (MissingMethodException) {} // no default exists for this type, fall-through to returning null
}
return default(T);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With