Получить общий тип вызова метода в динамическом объекте
Я начинаю работать с динамическими объектами в .Net, и я не могу понять, как что-то сделать.
У меня есть класс, наследующий от DynamicObject, и я переопределяю метод TryInvokeMember.
например.
class MyCustomDynamicClass : DynamicObject
{
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
// I want to know here the type of the generic argument
}
}
И внутри этого метода я хочу знать тип (если есть) общих аргументов в вызове.
например.
Если я вызываю следующий код, я хочу получить значение System.Boolean и System.Int32 внутри переопределенного метода моего динамического объекта
dynamic myObject = new MyCustomDynamicClass();
myObject.SomeMethod<bool>("arg");
myObject.SomeOtherMethod<int>("arg");
В настоящее время, если я помещаю точку останова внутри переопределенного метода, я могу получить имя вызываемого метода ( "SomeMethod" и "SomeOtherMethod", а также значения аргументов, но не общие типы).
Как я могу получить эти значения?
Спасибо!
Ответы
Ответ 1
Фактически я просмотрел иерархию связующего и нашел свойство с необходимыми значениями во внутренних полях объекта.
Проблема заключается в том, что свойство не отображается, поскольку оно использует С# -специфический код/классы, поэтому свойства должны быть доступны с помощью Reflection.
Я нашел код в этом японском блоге: http://neue.cc/category/programming (я не читаю никаких японских, поэтому я не уверен, что автор фактически описывает эту же проблему
Здесь фрагмент:
var csharpBinder = binder.GetType().GetInterface("Microsoft.CSharp.RuntimeBinder.ICSharpInvokeOrInvokeMemberBinder");
var typeArgs = (csharpBinder.GetProperty("TypeArguments").GetValue(binder, null) as IList<Type>);
typeArgs - это список, содержащий типы общих аргументов, используемых при вызове метода.
Надеюсь, это поможет кому-то еще.
Ответ 2
Рамка с открытым исходным кодом Dynamitey может вызывать свойства, внутренние/защищенные/частные, используя DLR и, таким образом, работает с Silverlight. Но это немного сложно с явными членами интерфейса, поскольку вам нужно использовать фактическое полное имя члена в типе, а не имя члена интерфейса. Итак, вы можете сделать:
var typeArgs = Dynamic.InvokeGet(binder, "Microsoft.CSharp.RuntimeBinder.ICSharpInvokeOrInvokeMemberBinder.TypeArguments")
as IList<Type>;
Ответ 3
Немного googling, и у меня есть довольно общее решение для .NET и Mono:
/// <summary>Framework detection and specific implementations.</summary>
public static class FrameworkTools
{
private static bool _isMono = Type.GetType("Mono.Runtime") != null;
private static Func<InvokeMemberBinder, IList<Type>> _frameworkTypeArgumentsGetter = null;
/// <summary>Gets a value indicating whether application is running under mono runtime.</summary>
public static bool IsMono { get { return _isMono; } }
static FrameworkTools()
{
_frameworkTypeArgumentsGetter = CreateTypeArgumentsGetter();
}
private static Func<InvokeMemberBinder, IList<Type>> CreateTypeArgumentsGetter()
{
if (IsMono)
{
var binderType = typeof(Microsoft.CSharp.RuntimeBinder.RuntimeBinderException).Assembly.GetType("Microsoft.CSharp.RuntimeBinder.CSharpInvokeMemberBinder");
if (binderType != null)
{
ParameterExpression param = Expression.Parameter(typeof(InvokeMemberBinder), "o");
return Expression.Lambda<Func<InvokeMemberBinder, IList<Type>>>(
Expression.TypeAs(
Expression.Field(
Expression.TypeAs(param, binderType), "typeArguments"),
typeof(IList<Type>)), param).Compile();
}
}
else
{
var inter = typeof(Microsoft.CSharp.RuntimeBinder.RuntimeBinderException).Assembly.GetType("Microsoft.CSharp.RuntimeBinder.ICSharpInvokeOrInvokeMemberBinder");
if (inter != null)
{
var prop = inter.GetProperty("TypeArguments");
if (!prop.CanRead)
return null;
var objParm = Expression.Parameter(typeof(InvokeMemberBinder), "o");
return Expression.Lambda<Func<InvokeMemberBinder, IList<Type>>>(
Expression.TypeAs(
Expression.Property(
Expression.TypeAs(objParm, inter),
prop.Name),
typeof(IList<Type>)), objParm).Compile();
}
}
return null;
}
/// <summary>Extension method allowing to easyly extract generic type arguments from <see cref="InvokeMemberBinder"/>.</summary>
/// <param name="binder">Binder from which get type arguments.</param>
/// <returns>List of types passed as generic parameters.</returns>
public static IList<Type> GetGenericTypeArguments(this InvokeMemberBinder binder)
{
// First try to use delegate if exist
if (_frameworkTypeArgumentsGetter != null)
return _frameworkTypeArgumentsGetter(binder);
if (_isMono)
{
// In mono this is trivial.
// First we get field info.
var field = binder.GetType().GetField("typeArguments", BindingFlags.Instance |
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
// If this was a success get and return it value
if (field != null)
return field.GetValue(binder) as IList<Type>;
}
else
{
// In this case, we need more aerobic :D
// First, get the interface
var inter = binder.GetType().GetInterface("Microsoft.CSharp.RuntimeBinder.ICSharpInvokeOrInvokeMemberBinder");
if (inter != null)
{
// Now get property.
var prop = inter.GetProperty("TypeArguments");
// If we have a property, return it value
if (prop != null)
return prop.GetValue(binder, null) as IList<Type>;
}
}
// Sadly return null if failed.
return null;
}
}
Получайте удовольствие. Кстати, Impromptu классный, но я не могу его использовать.