17

.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」、および引数の値ですが、ジェネリック型は取得できません)。

これらの値を取得するにはどうすればよいですか?

ありがとう!

4

3 に答える 3

12

実際、バインダーの階層を調べて、オブジェクトの内部フィールドに必要な値を持つプロパティを見つけました。

問題は、C#固有のコード/クラスを使用しているため、プロパティが公開されていないことです。したがって、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は、メソッドを呼び出すときに使用される一般的な引数のタイプを含むリストです。

これが他の誰かに役立つことを願っています。

于 2011-03-30T22:17:54.563 に答える
3

少しグーグルすると、.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はかっこいいですが、使えません。

于 2012-07-31T10:01:00.027 に答える
3

オープン ソース フレームワークのDynamiteyは、DLR を使用して内部/保護/非公開のプロパティを呼び出すことができるため、Silverlight で動作します。ただし、インターフェイスのメンバー名ではなく、型のメンバーの実際の完全な名前を使用する必要があるため、インターフェイスの明示的なメンバーでは少し注意が必要です。したがって、次のことができます。

var typeArgs = Dynamic.InvokeGet(binder, "Microsoft.CSharp.RuntimeBinder.ICSharpInvokeOrInvokeMemberBinder.TypeArguments")
     as IList<Type>;
于 2011-04-09T20:54:35.107 に答える