Question

Je commence à travailler avec des objets dynamiques dans .net et je ne peux pas comprendre comment faire quelque chose.

J'ai une classe qui hérite de DynamicObject et je remplace la méthode TryinvokeMember.

E.g.

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

et à l'intérieur de cette méthode, je veux connaître le type (le cas échéant) des arguments génériques de l'invocation.

E.g. Si j'invoque le code suivant, je souhaite obtenir la valeur de System.Booleren et System.int32 à l'intérieur de la méthode annulée de mon objet dynamique

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

Actuellement, si je place un point d'arrêt à l'intérieur de la méthode annulée, je peux obtenir le nom de la méthode appelée ("SOMEMETHOD" et "quelque athlette", ainsi que les valeurs des arguments, mais pas les types génériques).

Comment puis-je obtenir ces valeurs?

merci!

Était-ce utile?

La solution

Actually I looked through the hierarchy of the binder and found a property with the needed values in the internal fields of the object.

The problem is that the property isn't exposed because it uses C#-specific code/classes, therefore the properties must be accessed using 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

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.

Hope this helps someone else.

Autres conseils

A bit of googling and I have quite generic solution for .NET and 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.

The open source framework Dynamitey can call properties that internal/protected/private using the DLR and thus works with 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>;
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top