2011-03-30 3 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"); 

의 오버라이드 메서드 내 선택 System.Boolean 및 선택 System.Int32의 값을 얻으려면 호출되는 메소드의 이름 ("SomeMethod"및 "SomeOtherMethod"및 인수의 값은 있지만 generic 유형은 제외).

어떻게 이러한 값을 얻을 수 있습니까?

감사합니다.

+0

대부분의 경우 리플렉션을 사용하여 메소드를 검색해야합니다. MethodInfo는 제네릭 형식 인수에 대한 액세스를 제공합니다. –

+0

문제는 메서드가 존재하지 않는다는 것입니다. 제네릭 정보가없는 CallInfo 속성이있는 바인더 개체에 액세스 할 수 있습니다. – willvv

+1

알다시피, 나는 이것에 대한 샘플을 잠시 동안 사용 해왔다. 그리고 일반적인 정보가 어디에 있는지 알 수 없다. 이것은 실제로 정말로 좋은 질문입니다. – Tejs

답변

11

사실 저는 바인더의 계층을 살펴본 결과 객체의 내부 필드에 필요한 값을 가진 속성을 발견했습니다.

문제는 C# 관련 코드/클래스를 사용하므로 속성이 노출되지 않으므로 Reflection을 사용하여 속성에 액세스해야한다는 점입니다.

나는이 일본어 블로그의 코드를 발견

var csharpBinder = binder.GetType().GetInterface("Microsoft.CSharp.RuntimeBinder.ICSharpInvokeOrInvokeMemberBinder"); 
var typeArgs = (csharpBinder.GetProperty("TypeArguments").GetValue(binder, null) as IList<Type>); 
: 저자가 실제로이 같은 문제가 여기에

에게 설명하는 조각의 경우 http://neue.cc/category/programming (나는 그러므로 나는 확실하지 않다, 어떤 일본어를 읽지 않는다

typeArgs는 메소드를 호출 할 때 사용되는 일반적인 인수의 유형을 포함하는 목록입니다.

희망이 다른 사람을 도움이됩니다.

+0

잘 했어 :) 예쁜 일은 아니지만 그 일을 해! –

+0

사실이 코드에서 catch를 발견했습니다. 그것은 프레임 워크의 전체 버전에서만 작동하며 Silverlight에서 사용해야하므로 실제 대답이 아직 누락되었습니다. ( – willvv

3

오픈 소스 프레임 워크 Dynamitey은 DLR을 사용하여 내부/보호/개인 속성을 호출 할 수 있으므로 Silverlight에서 작동합니다. 그러나 인터페이스 멤버 이름 대신 해당 유형에 대한 멤버의 실제 전체 이름을 사용해야하므로 인터페이스 명시 적 멤버는 약간 까다 롭습니다. 인터넷 검색의 비트

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

이것은 최고입니다 – albertjan

+0

바인더가 유형이 아니기 때문에이 기능이 작동하지 않습니다. ICSharpInvokeOrInvokeMemberBinder하지만 TypeArguments를 m_typeArguments로 변경하면 작동하지 않습니다. – albertjan

+0

__m_typeArguments가 작동하지만 해당 속성 버전과 다른 유형을 반환하기 때문에 인터페이스 캐스팅 예제를 제거했습니다. – jbtule

3

을 나는 .NET 및 모노에 대한 매우 일반적인 솔루션이 있습니다 : 그래서 당신은 할 수

/// <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는 멋지지만 사용할 수는 없습니다.

관련 문제