Ось мій метод передавання об'єкта, але не до загальної змінної типу, а System.Type
динамічно:
Я створюю лямбда-вираз під час виконання, використовуючи System.Linq.Expressions
, типу Func<object, object>
, який розпаковує його введення, виконує перетворення потрібного типу, а потім дає результат в полі. Новий потрібен не лише для всіх типів, на які потрапляють касти, а й для типів, які отримують кастинг (через крок розпакування). Створення цих виразів забирає багато часу через відображення, компіляцію та побудову динамічного методу, що робиться під кришкою. На щастя, колись створені, вирази можна викликати неодноразово і без великих накладних витрат, тому я кешую кожен з них.
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);
}
Зауважте, що це не магія. Кастинг не відбувається в коді, як це робиться з dynamic
ключовим словом, перетворюються лише основні дані об'єкта. Під час компіляції нам залишається кропітко розібратися, яким саме типом може бути наш об’єкт, що робить це рішення непрактичним. Я написав це як хак, щоб викликати операторів перетворення, визначених довільними типами, але, можливо, хтось там може знайти кращий варіант використання.