Pokud daný typ dopředu neznáme (je nám předaný napřííklad jako parametr metody), musíme si pomoci jinak - ke slovu přichází reflexe. K tomuto účelu používám statickou helper třídu TypeExtensionsMethods, která jak název napovídá obsahuje extension metody pro instance typu Type a přidává třídě Type metodu GetDefaultValue().
Pro typy, které nejsou hodnotové je výsledek jednoduchý - null, u hodnotových typů si pomůžeme zavoláním generické metody GetDefaultGenericValue() pomoci reflexe. A vzhledem k tomu, že reflexe má dopady na výkon, je dobré si takovéto hodnoty kešovat pro budoucí volání. K tomuto účelu se hodí ukládání do ConcurrentDictionary, který je thread-safe, takže nemusíme řešit zámky v případě vícevláknového volání.
Pozn.: Metoda GetDefaultGenericValue() by mohla být private, protože její volání nikde jinde nepoužívám (ani nemá význam vzhledem k existenci systémového default(T)). Jelikož mám nainstalovaný doplněk Resharper ve Visual Studiu, který nevidí použití metody přes reflexi a přesvědčuje mě, že je dobrý nápad tuto nevyužitou metodu smazat, tak jsem ji označil pro jednoduchost jako public.
using System.Collections.Concurrent;
using System.Reflection;
namespace System
{
public static class TypeExtensionsMethods
{
private static readonly ConcurrentDictionary<Type, object> TypeDefaults = new ConcurrentDictionary<Type, object>();
public static T GetDefaultGenericValue<T>()
{
return default(T);
}
public static object GetDefaultValue(this Type type)
{
if (type == null) throw new ArgumentNullException("type");
return type.IsValueType
? TypeDefaults.GetOrAdd(type, t => typeof(TypeExtensionsMethods).GetMethod("GetDefaultGenericValue", BindingFlags.Static | BindingFlags.Public).MakeGenericMethod(t).Invoke(null, null))
: null;
}
}
}
Pokud někde v kódu potřebuju získat defaultní hodnotu pro typ předaný prametrem, stačí postupovat takto.
private void Foo(Type type)
{
var defaultValue = type.GetDefaultValue();
Console.WriteLine(defaultValue);
}
Klasik by zvolal: "Jak snadné, milý Watsone!" :-)