【问题标题】:Get generic type of call to method in dynamic object获取动态对象中方法调用的通用类型
【发布时间】:2011-07-26 10:18:30
【问题描述】:

我开始在 .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”,以及参数的值,但不是泛型类型)。

我怎样才能得到这些值?

谢谢!

【问题讨论】:

  • 您很可能需要使用反射查找方法。 MethodInfo 提供对泛型类型参数的访问。
  • 问题是方法不存在,我只是可以访问binder对象,它有一个CallInfo属性,没有任何泛型信息。
  • 你知道,我已经尝试了一段时间的样本,但我也找不到通用信息在哪里。这实际上是一个非常好的问题。

标签: c# generics dynamic dynamicobject


【解决方案1】:

实际上,我查看了 binder 的层次结构,并在对象的内部字段中找到了具有所需值的属性。

问题在于该属性未公开,因为它使用 C# 特定的代码/类,因此必须使用反射访问这些属性。

我在这个日文博客中找到了代码:http://neue.cc/category/programming(我没有读过任何日文,因此我不确定作者是否真的描述了同样的问题

这是sn-p:

var csharpBinder = binder.GetType().GetInterface("Microsoft.CSharp.RuntimeBinder.ICSharpInvokeOrInvokeMemberBinder");
var typeArgs = (csharpBinder.GetProperty("TypeArguments").GetValue(binder, null) as IList<Type>);

typeArgs 是一个列表,其中包含调用方法时使用的通用参数的类型。

希望这对其他人有所帮助。

【讨论】:

  • 干得好 :) 不漂亮,但做得很好!
  • 其实我在这段代码中发现了一个问题。它只适用于完整版的框架,我需要在 Silverlight 中使用它,因此我仍然缺少真正的答案:(
【解决方案2】:

有点谷歌搜索,我有 .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's 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's value
                if (prop != null)
                    return prop.GetValue(binder, null) as IList<Type>;
            }
        }

        // Sadly return null if failed.
        return null;
    }
}

玩得开心。顺便说一下 Impromptu 很酷,但我不能用它。

【讨论】:

    【解决方案3】:

    开源框架Dynamitey 可以使用 DLR 调用内部/受保护/私有的属性,因此可以与 Silverlight 一起使用。但是接口显式成员有点棘手,因为您必须在类型上使用成员的实际全名,而不是接口成员名称。所以你可以这样做:

    var typeArgs = Dynamic.InvokeGet(binder, "Microsoft.CSharp.RuntimeBinder.ICSharpInvokeOrInvokeMemberBinder.TypeArguments")
         as IList<Type>;
    

    【讨论】:

    • 您好,这不起作用,因为 binder 不是 ICSharpInvokeOrInvokeMemberBinder 类型,但如果您将 TypeArguments 更改为 m_typeArguments,它确实有效。
    • 呃,你是对的,虽然 m_typeArguments 有效,但它返回的类型与该属性版本不同,所以我删除了接口鸭子转换示例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多