简体   繁体   English

获取动态对象中方法的泛型类型调用

[英]Get generic type of call to method in dynamic object

I'm starting to work with dynamic objects in .Net and I can't figure out how to do something. 我开始使用.Net中的动态对象,我无法弄清楚如何做某事。

I have a class that inherits from DynamicObject, and I override the TryInvokeMember method. 我有一个继承自DynamicObject的类,我重写了TryInvokeMember方法。

eg 例如

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
    }
}

And inside that method I want to know the type (if any) of the generic arguments in the invocation. 在该方法中,我想知道调用中泛型参数的类型(如果有的话)。

eg If I invoke the following code, I want to get the value of System.Boolean and System.Int32 inside the overrided method of my dynamic object 例如,如果我调用以下代码,我想在动态对象的覆盖方法中获取System.Boolean和System.Int32的值

dynamic myObject = new MyCustomDynamicClass();
myObject.SomeMethod<bool>("arg");
myObject.SomeOtherMethod<int>("arg");

Currently if I place a breakpoint inside the overrided method I can get the name of the method being invoked ("SomeMethod" and "SomeOtherMethod", and also the values of the arguments, but not the generic types). 目前,如果我在覆盖的方法中放置一个断点,我可以得到被调用方法的名称(“SomeMethod”和“SomeOtherMethod”,以及参数的值,但不是泛型类型)。

How can I get these values? 我怎样才能获得这些价值?

Thanks! 谢谢!

Actually I looked through the hierarchy of the binder and found a property with the needed values in the internal fields of the object. 实际上,我查看了binder的层次结构,并在对象的内部字段中找到了具有所需值的属性。

The problem is that the property isn't exposed because it uses C#-specific code/classes, therefore the properties must be accessed using Reflection. 问题是该属性未公开,因为它使用C#特定的代码/类,因此必须使用Reflection访问属性。

I found the code in this japanese blog: http://neue.cc/category/programming (I don't read any japanese, therefore I'm not sure if the author actually describes this same issue 我在这个日本博客中找到了代码: http//neue.cc/category/programming (我不读任何日语,因此我不确定作者是否真的描述了同样的问题

Here's the snippet: 这是片段:

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

typeArgs is a list containing the types of the generic arguments used when invoking the method. typeArgs是一个列表,包含调用方法时使用的泛型参数的类型。

Hope this helps someone else. 希望这有助于其他人。

A bit of googling and I have quite generic solution for .NET and Mono: 有点谷歌搜索,我有非常通用的.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;
    }
}

Have fun. 玩得开心。 By the way Impromptu is cool, but I can't use it. 顺便说一下,Impromptu很酷,但我无法使用它。

The open source framework Dynamitey can call properties that internal/protected/private using the DLR and thus works with Silverlight. 开源框架Dynamitey可以使用DLR调用internal / protected / private属性,因此可以使用Silverlight。 But it get's a little tricky with interface explicit members as you have to use the actual full name of the member on the the type, rather than the interface member name. 但是接口显式成员有点棘手,因为你必须在类型上使用成员的实际全名,而不是接口成员名。 So you can do: 所以你可以这样做:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM