什么';这是使C#代码在.NET中后期绑定的最小侵入性方法<;4.

什么';这是使C#代码在.NET中后期绑定的最小侵入性方法<;4.,c#,.net,late-binding,C#,.net,Late Binding,我正在编写一段处理Windows窗体控件的C代码。下面是一个小示例,一个用于获取某些控件的边界矩形(在屏幕坐标中)的小包装器: public class GUIObject { protected Control m_control; // [..] public virtual Rectangle Bounds { get { Rectangle r = m_control.Bounds; if ( m

我正在编写一段处理Windows窗体控件的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窗体版本。对于.NET4,我可以使用
dynamic
关键字,但遗憾的是,这段代码也应该适用于.NET3应用程序。因此,我开始使用反射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
的思路使用了包装类,还是走了一条完全不同的路线?就原始代码而言,我正在寻找侵入性最小的方法。

我不明白。我将.NET 4控件传递给根据.NET 2编译的DLL,它们工作得很好。

使用帮助程序扩展进行反射:

 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);
  }
}
var r=m_控制._P(“边界”)??新矩形();
var母公司=母公司控制权;
如果(父项!=null)
r=父项。_M(“矩形屏幕”,r);
静态公共类反射HLP2
{
公共静态T\u P(此对象项,字符串名称)
{
如果(项==null)
返回默认值(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();
如果(members.Length==0)
返回默认值(T);
如果(members.Length>1)
抛出新异常(string.Format(“Убъбббббб/Сббббббббббббббббб;
var member=members.First();
客观结果;
if(成员为FieldInfo)
结果=((FieldInfo)成员).GetValue(项);
其他的
结果=((PropertyInfo)成员).GetValue(项,null);
如果(结果为T)
返回(T)结果;
返回默认值(T);
}
公共静态void\u P(此对象项、字符串名称、T值)
{
如果(项==null)
返回;
var type=item.GetType();
var members=type.GetMembers(BindingFlags.GetField | BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(_member=>_member.Name==Name)
.ToArray();
如果(members.Length==0)
返回;
如果(members.Length>1)
抛出新异常(string.Format(“Убъбббббб/Сббббббббббббббббб;
var member=members.First();
if(成员为FieldInfo)
((FieldInfo)成员).SetValue(项,值);
其他的
((PropertyInfo)成员).SetValue(项,值,null);
}
公共静态void _M(此对象项,字符串名称,参数对象[]args)
{
_M(项目、名称、参数);
}
公共静态T_M(此对象项,字符串名称,参数对象[]args)
{
如果(项==null)
返回默认值(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)
返回默认值(T);
如果(methods.Length>1)
抛出新异常(string.Format(“string.Format”(“string.Format”(“string.Format”(“string.Format”),“string.Format”(“string.Format”),“string.Format”(“string.Format”),“string.Format”(“string.Format”),“string.Format”(“string.Format”),“string;
var method=methods.First();
var result=method.Invoke(item,args);
如果(结果为T)
返回(T)结果;
返回默认值(T);
}
}

一个想法是为对象的外观创建接口,然后使用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);
接口IGUIObject
{
矩形边界{get;}
矩形矩形到屏幕(矩形边界);
IGUIObject父对象{get;}
}
var obj=GetInstance();
var proxy=反射.强制(obj);
返回proxy.Parent.RectangleToScreen(proxy.Bounds);
我在这里发布了一篇博客文章,其中介绍了如何进行动态强制的简单起点,包括示例应用程序:

有趣的是,使用这种技术,您实际上可以消除每次调用反射,这在性能方面非常昂贵。相反,您只需在代理生成器中执行一次反射,而您生成的实际上会在反射之后直接调用相应的属性/方法/字段。同样使用此技巧,当您删除对代理实例的引用时,生成的动态程序集将被卸载。您可以缓存类型生成的类型,以使后续代理创建非常快

你的情况比我的小样本更复杂,但我认为你可以走得更远