Ecco il mio metodo per lanciare un oggetto ma non in una variabile di tipo generico, piuttosto in modo System.Type
dinamico:
Creo un'espressione lambda in fase di runtime usando System.Linq.Expressions
, di tipo Func<object, object>
, che deseleziona il suo input, esegue la conversione del tipo desiderata e poi dà il risultato in scatola. Ne è necessario uno nuovo non solo per tutti i tipi su cui viene eseguito il cast, ma anche per i tipi su cui viene eseguito il cast (a causa del passaggio di unboxing). La creazione di queste espressioni richiede molto tempo, a causa della riflessione, della compilazione e della costruzione del metodo dinamico che viene eseguita sotto il cofano. Fortunatamente, una volta create, le espressioni possono essere invocate ripetutamente e senza spese generali elevate, quindi memorizzo nella cache ognuna.
private static Func<object, object> MakeCastDelegate(Type from, Type to)
{
var p = Expression.Parameter(typeof(object)); //do not inline
return Expression.Lambda<Func<object, object>>(
Expression.Convert(Expression.ConvertChecked(Expression.Convert(p, from), to), typeof(object)),
p).Compile();
}
private static readonly Dictionary<Tuple<Type, Type>, Func<object, object>> CastCache
= new Dictionary<Tuple<Type, Type>, Func<object, object>>();
public static Func<object, object> GetCastDelegate(Type from, Type to)
{
lock (CastCache)
{
var key = new Tuple<Type, Type>(from, to);
Func<object, object> cast_delegate;
if (!CastCache.TryGetValue(key, out cast_delegate))
{
cast_delegate = MakeCastDelegate(from, to);
CastCache.Add(key, cast_delegate);
}
return cast_delegate;
}
}
public static object Cast(Type t, object o)
{
return GetCastDelegate(o.GetType(), t).Invoke(o);
}
Nota che questa non è magia. Il cast non avviene nel codice, come accade con la dynamic
parola chiave, vengono convertiti solo i dati sottostanti dell'oggetto. Al momento della compilazione siamo ancora lasciati a capire con cura esattamente quale tipo potrebbe essere il nostro oggetto, rendendo questa soluzione poco pratica. Ho scritto questo come un trucco per invocare operatori di conversione definiti da tipi arbitrari, ma forse qualcuno là fuori può trovare un caso d'uso migliore.