2011-03-30 67 views
15

我開始在.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 
    } 
} 

這方法我想知道在調用泛型參數的類型(如果有的話)內。

例如 如果我調用下面的代碼,我想我的動態對象

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

目前的overrided方法內部System.Boolean和System.Int32的價值,如果我把一個斷點overrided方法,我可以進去被調用的方法名稱(「SomeMethod」和「SomeOtherMethod」,以及參數值,但不是泛型類型)。

如何獲得這些值?

謝謝!

+0

您很可能需要使用反射來查找該方法。 MethodInfo提供對泛型類型參數的訪問。 – 2011-03-30 21:10:34

+0

問題是該方法不存在,我只能訪問binder對象,它有一個CallInfo屬性,它沒有任何泛型信息。 – willvv 2011-03-30 21:26:17

+1

你知道,我一直在試用這個樣品一段時間,而且我也找不到通用信息。這實際上是一個非常好的問題。 – Tejs 2011-03-30 21:35:18

回答

11

實際上,我查看了活頁夾的層次結構,並在對象的內部字段中找到了具有所需值的屬性。

問題是該屬性未公開,因爲它使用特定於C#的代碼/類,因此必須使用反射訪問屬性。

我發現的代碼在這個日本博客: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是包含類型的調用方法時所使用的通用參數列表。

希望這可以幫助其他人。

+0

幹得好:)不漂亮,但幹得不錯! – 2011-03-30 22:48:26

+0

其實我在這段代碼中發現了一個catch。它只適用於完整版本的框架,我需要在Silverlight中使用它,因此我仍然錯過了一個真正的答案:( – willvv 2011-03-31 00:30:37

3

開源框架Dynamitey可以使用DLR調用內部/保護/私有屬性,因此可以與Silverlight一起使用。但是,對於接口顯式成員而言,它有點棘手,因爲您必須在類型上使用成員的實際完整名稱,而不是接口成員名稱。所以,你可以這樣做:

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

這真棒! – albertjan 2011-09-06 10:32:06

+0

嗨,這不工作,因爲活頁夾是不是類型ICSharpInvokeOrInvokeMemberBinder,但是如果你將TypeArguments改爲m_typeArguments,它確實有效。 – albertjan 2011-09-06 11:22:10

+0

呃,你說的對,雖然m_typeArguments有效,但它返回的屬性版本不同,所以我刪除了接口的鴨子示例。 – jbtule 2011-09-07 14:06:30

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很酷,但我不能使用它。