.NET <4에서 c # 코드를 늦게 만들 수있는 가장 중요한 방법은 무엇입니까?

StackOverflow https://stackoverflow.com//questions/9670466

  •  12-12-2019
  •  | 
  •  

문제

Windows Forms 컨트롤을 처리하는 C # 코드 조각에서 작업하고 있습니다. 다음은 일부 컨트롤을 위해 경계 사각형 (화면 좌표로)을 얻는 작은 예제입니다.

public class GUIObject {
    protected Control m_control;

    // [..]

    public virtual Rectangle Bounds {
        get {
            Rectangle r = m_control.Bounds;
            if ( m_control.Parent != null ) {
                return m_control.Parent.RectangleToScreen( r );
            }
            return r;
        }
    }
}
.

이 코드는 고객 응용 프로그램에로드 될 "플러그인"으로 분산 된 라이브러리로 컴파일됩니다. 그러나 일부 고객이 내 플러그인이 링크 된 것보다 응용 프로그램에서 다른 버전의 Windows 양식을 사용했음을 밝혀졌습니다. 내 계획은 위의 코드를 늦게 만들어이를 처리하여 현재 응용 프로그램 도메인에 Windows Forms 버전이로드되는 것으로 작동 할 것입니다. .NET 4를 사용하면 dynamic 키워드를 사용할 수 있지만이 코드는 .NET3 응용 프로그램에서도 작동해야합니다. 따라서 나는 반사 API를 사용하기 시작하여 리플렉션 API를 사용하는 작은 헬퍼 객체를 도입하여 리플렉션 API를 조금 더 멋지게 사용합니다.

public class LateBoundObject {
    private Object m_o;

    // [..]

    public Object GetProperty( String name ) {
        PropertyInfo pi = m_o.GetType().GetProperty( name );
        return pi == null ? null
                          : pi.GetValue( m_o, null );
    }

    public Object InvokeMethod( String name, Object[] args ) {
        MethodInfo mi = m_o.GetType().GetMethod( name );
        return mi == null ? null
                          : mi.Invoke( m_o, args );
    }
}

public class GUIObject {
    protected LateBoundObject m_control;

    // [..]

    public virtual Rectangle Bounds {
        get {
            Object r = m_control.GetProperty( "Bounds" );
            if ( r == null) {
                return new Rectangle();
            }

            Object parent = m_control.GetProperty( "Parent" );
            if ( parent != null ) {
                LateBoundObject po = new LateBoundObject( parent );
                r = po.InvokeMethod( "RectangleToScreen",
                                     new Object[] { r } );
            }
            return (Rectangle)r;
        }
    }
}
.

별로 예쁘지 않습니다. 발신자면에 필요한 많은 주조가 필요하며, 나는 오버로드 된 방법이나 속성을 더 빨리 또는 나중에 처리해야합니다. 꽤 울퉁불퉁 한 타는 것입니다. 이상적으로, 래퍼 객체는 원래 코드를 매우 똑같이 유지할 수 있습니다.

그래서, LateBoundObject 래퍼 클래스를 고정하기 전에 나는 궁금해합니다 : 다른 사람이 반사 API를 사용하여 C # 코드를 늦게 만드는 데 경험이 있습니까? 그렇다면 원시 반사 API를 최소한으로 사용하는 고통을 유지하기 위해 어떻게 접근 했습니까? - LateBoundObject 라인을 따라 래퍼 클래스를 사용 했습니까? 아니면 완전히 다른 경로를 사용 했습니까? 원래 코드가 염려되는 한 최소 침습적 인 방법을 찾고 있습니다.

도움이 되었습니까?

해결책

하나의 아이디어는 오브젝트가 객체를 원하는 것과 같은 인터페이스를 작성하는 것 다음으로 system.reflection.emit을 사용하여 실제 인스턴스를 부합시킬 수있는 클래스를 생성합니다. 인터페이스 메소드에서 실제 인스턴스로 호출하는 호출을 프록시하는 동적으로 생성 된 객체로 랩핑 하여이 작업을 수행 할 수 있습니다.

사용법은 다음과 같이 보일 것입니다 :

interface IGUIObject 
{
  Rectangle Bounds { get; }
  Rectangle RectangleToScreen(Rectangle bounds);
  IGUIObject Parent { get; }
}

var obj = GetInstance();
var proxy = Reflection.Coerce<IGUIObject>(obj);
return proxy.Parent.RectangleToScreen(proxy.Bounds);
.

샘플 앱을 포함하여 동적 강압을 수행하는 방법을위한 간단한 출발점이있는 블로그 게시물이 있습니다. 강요 유형 및 어셈블리 언로드

흥미로운 점은이 기술을 사용하여 실제로 통화당 성능이 현명한 통화 별 반사를 제거 할 수 있다는 것입니다. 대신 프록시 생성기에서 한 번 반성을 수행하고 생성 된 내용은 실제로 해당 속성 / 메소드 / 필드를 직접 호출합니다. 또한이 속임수를 사용하여 프록시 인스턴스에 대한 참조를 삭제하면 생성 된 동적 어셈블리가 언로드됩니다. 생성 된 유형 생성 유형을 캐시하여 후속 프록시 작성을 매우 빠르게 만들 수 있습니다.

귀하의 상황은 내 작은 샘플보다 복잡하지만 출발점으로서 매우 멀리 떨어져있을 수 있다고 생각합니다.

다른 팁

나는 그것을 얻지 못한다..NET 2에 대해 컴파일 된 DLL에 대한 .NET 4 컨트롤을 통과하고 정확하게 작동합니다.

반사를 위해 도우미 확장을 사용하십시오 :

 var r = m_control._P<Rectangle>("Bounds") ?? new Rectangle();
 var parent = m_control._P<Control>("Parent");
 if (parent != null)
   r = parent._M<Rectangle>("RectangleToScreen", r);



static public class ReflectionHlp2
{
  public static T _P<T>(this object item, string name)
  {
    if (item == null)
      return default(T);
    var type = item.GetType();

    var members = type.GetMembers(BindingFlags.GetField | BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
      .Where(_member => _member.Name == name)
      .ToArray();
    if (members.Length == 0)
      return default(T);
    if (members.Length > 1)
      throw new Exception(string.Format("У объекта полей/свойств с именем '{0}' больше чем один: '{1}'", name, members.Length));
    var member = members.First();
    object result;
    if (member is FieldInfo)
      result = ((FieldInfo)member).GetValue(item);
    else
      result = ((PropertyInfo)member).GetValue(item, null);
    if (result is T)
      return (T)result;
    return default(T);
  }
  public static void _P<T>(this object item, string name, T value)
  {
    if (item == null)
      return;
    var type = item.GetType();

    var members = type.GetMembers(BindingFlags.GetField | BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
      .Where(_member => _member.Name == name)
      .ToArray();
    if (members.Length == 0)
      return;
    if (members.Length > 1)
      throw new Exception(string.Format("У объекта полей/свойств с именем '{0}' больше чем один: '{1}'", name, members.Length));
    var member = members.First();
    if (member is FieldInfo)
      ((FieldInfo)member).SetValue(item, value);
    else
      ((PropertyInfo)member).SetValue(item, value, null);
  }
  public static void _M(this object item, string name, params object[] args)
  {
    _M<object>(item, name, args);
  }
  public static T _M<T>(this object item, string name, params object[] args)
  {
    if (item == null)
      return default(T);
    var type = item.GetType();

    var methods = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
      .Where(_member => _member.Name == name)
      .ToArray();
    if (methods.Length == 0)
      return default(T);
    if (methods.Length > 1)
      throw new Exception(string.Format("Вызов перегруженных методов не поддерживается, у объекта методов с именем '{0}' больше чем один: '{1}'.", name, methods.Length));
    var method = methods.First();
    var result = method.Invoke(item, args);
    if (result is T)
      return (T)result;
    return default(T);
  }
}
.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top