在运行时重新定义基类

时间:2014-10-03 16:29:19

标签: c# .net

程序集A包含对程序集B的引用。程序集A类的一个子类 - Class1 - 在程序集B中。程序集C也包含相同的基类 - Class1。作为Class1重写的不同实现和可能不同的虚拟方法也是子类。有一分钟我们假设我很生气,并希望在运行时替换基类实现,替换程序集A中引用的内容。

我已调查assembly redirection失败,因为程序集B已签名且程序集C未签名。

我无法反编译代码并合并程序集,因为要解决的问题太多,例如 not ,所有类的定义都很容易被发现。由于需要子类化并且需要反编译,我无法使用CSharpCodeProvider类。 Reflection.Emit过于复杂,我需要反编译。

一个选项是获取子类的副本并根据某些开关进行动态编译,在代码中添加任何引用并将结果键入为动态。然后子类生活在一个动态对象中,但由于与另一个系统的交互,我有这种可怕的感觉,它需要在编译时或至少早一点可用。该代码由另一个系统调用,该系统可能在启动时或其他任意点使用Assembly.Load加载被覆盖的程序集。

使用Assembly.Load动态加载程序集引发了各种问题,你甚至可以用这种方式用反射代替基类吗?

有人知道可行的解决方案吗?

编辑: 更多上下文 - 问题是我们覆盖了一些虚拟方法,这些方法可以推广到数千个方框。几百个盒子需要不同的基类实现,目前必须完成2次部署才能解决这个问题,并应用各种脚本。目标是为所有部署提供二进制相同的dll,并且最好找到一种方法,使用config在需要时切换实现,而不是在数千次安装中更改dll,这需要重新启动软件可能会中断业务和创建支持问题。之前已经犯了一个错误,我无法改变单片架构。

2 个答案:

答案 0 :(得分:1)

解决方案不是子类。根据定义,is-a依赖关系是静态的,应该保持不变。您希望实现的是通过interface将接口与实现分离并将依赖关系降级为has-a的良好候选者。然后,您可以使用delegation pattern来完成任务。

这是constructor injection的一个简单示例,这是dependency injection的一个特例。

interface IAnimal
{
    int DoSomething();
}

class Bobcat implements IAnimal
{
    private IAnimal _a;

    public Bobcat(IAnimal a) // inject the dependency through the constructor
    {
        this._a = a;
    }

    public int DoSomething() // implement IAnimal
    {
        return _a.DoSomething(); // delegate to _a
    }
}

构造函数注入的优点是您可以保证IAnimal实现。您现在可以为setter injection添加方法:

public SetAnimal(IAnimal a)
{
    this._a = a; // hot-swap the IAnimal implementation
}

您现在可以在构造(运行时)上设置实现,甚至在执行期间进行热交换。如果您在所有a个实例中共享一个Bobcat实例,则可以获得其他功能。

所有这一切的要点是用动态is-a依赖项替换静态has-a依赖项,这仍然会产生行为完全相同的实例(即,具有的方法其他类)。这里有关于delegation pattern的更多信息。

答案 1 :(得分:1)

听起来你的问题是@pid的解决方案是它不能与Activator.CreateInstance一起使用。

解决方案是在构造函数内部执行注入(如果可以调用它)。

我不会在我完全控制的系统中部署这个解决方案,您也可以通过将Activator.CreateInstance替换为某种运行时IL代来优化此代码(这将带来(字面上)几千倍的性能)。

public class Foo()
{
    private IImplementFoo _implimentation;
    public Foo()
    {
        var implimentationTypeName = ConfigurationManager.AppSettings["Foo"];
        var implimentationType = GetType(implimentationTypeName);
        _implimentation = (IImplementFoo) Activator.CreateInstance(implimentationType);

    }

    public void Bar()
    {
        _implimentation.Bar();
    }
}