c# - 使 C# 代码在 .NET < 4 中后期绑定(bind)的侵入性最小的方法是什么?

标签 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_control.Parent != null ) {
                return m_control.Parent.RectangleToScreen( r );
            }
            return r;
        }
    }
}

此代码被编译到一个库中,该库作为“插件”分发到客户应用程序中。然而,事实证明,一些客户在他们的应用程序中使用了与我的插件链接的版本不同的 Windows 窗体版本。我的计划是通过使上述代码后期绑定(bind)来解决这个问题,以便它可以与当前应用程序域中加载的任何 Windows 窗体版本一起使用。对于 .NET 4,我可以使用 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# 代码后期绑定(bind)的经验吗?如果是这样,您是如何处理的,以尽量减少使用原始反射 API 的痛苦 - 您是否也按照 LateBoundObject 的方式使用了包装器类,或者您是否走了一条完全不同的路线?就原始代码而言,我正在寻找侵入性最小的方法。

最佳答案

一种想法是为您想要的对象创建接口(interface),然后使用 System.Reflection.Emit 生成可以强制实际实例的类。您可以通过将其包装在动态生成的对象中来实现此目的,该对象将从其接口(interface)方法的调用代理到它所包装的实际实例。

用法看起来像这样:

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);

我在这里有一篇博客文章,其中包含如何进行动态强制转换的简单起点,包括示例应用程序:coercing types and unloading assemblies

有趣的是,通过这种技术,您实际上可以摆脱每次调用反射,这在性能方面是非常昂贵的。相反,您在代理生成器中进行一次反射,然后您生成的内容实际上直接调用相应的属性/方法/字段。此外,通过此技巧,当您删除对代理实例的引用时,生成的动态程序集将被卸载。您可以缓存类型生成的类型,以使后续代理创建速度非常快。

您的情况比我的小样本更复杂,但我认为您可以以此为起点走得很远。

关于c# - 使 C# 代码在 .NET < 4 中后期绑定(bind)的侵入性最小的方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9670466/

相关文章:

c# - iis重置后应用程序池被删除

c# - 什么是NullReferenceException,如何解决?

javascript - Function.prototype.bind() 总是很慢吗?

c# - 如何将整数数组传递给方法 : Reflection C#

c# - 函数调用决定函数的返回类型

c# - 为什么我的 ViewState 没有被保留?

c# - ninject 中具有多种实现的接口(interface)

c# - Nuget 安装包成功但未向 csproj 添加引用

.net - 十进制正则表达式问题

php - 是否有可能在 PHP 中过度使用后期静态绑定(bind)?