在运行时使用另一个基类更改/交换基类

时间:2010-12-10 17:00:23

标签: c# oop class

听起来很愚蠢,但我想知道它是否有可能。

是否可以在运行时更改派生类的基类?当然,有很多ifs和buts以及为什么会有人这样做以及它可能是一个糟糕的设计的问题。

将所有这些放在一边(即使它们可能完全有效),让我们说,只是为了踢或显示你的神经质,是否可能用C#或任何语言来解决这个问题?

类似于:

public class baseOriginal {
    public string justAProperty;
}

public class baseSwapped {
    public int sillyNumber;
}

public class derivedClass : baseOriginal {
   public bool iAmDumb;
}

void Main() {
    baseOriginal derived = new derivedClass ();
    Console.WriteLine(derived.justAProperty);
    baseSwapped derivedSwapped = (??);
    Console.WriteLine(derivedSwapped.sillyNumber);
}

6 个答案:

答案 0 :(得分:1)

在C#中是不可能的。您可能想要的更多是基于原型的解决方案,这种解决方案常见于JavaScript等动态语言中,您可以通过添加对象的功能来“扩展”对象的功能。

但要完成代码提示的操作,可以让swappable类继承自共同的祖先类。这样,您可以将每个实例分配给其后代。

public class baseClassAncestor{

}
public class baseOriginal:baseClassAncestor { 
    public string justAProperty; 
} 

public class baseSwapped:baseClassAncestor  { 
    public int sillyNumber; 
} 

public class derivedClass : baseOriginal { 
   public bool iAmDumb; 
} 

答案 1 :(得分:1)

您可以通过使用派生类加载实现基类BEFORE的不同程序集来执行一次基类交换。但是这种方法不会使你的确切代码工作,因为你将无法编译 - 但是可以使对不同基类的方法的访问移动到单独的函数。

添加包含所有基类中所有可能方法/属性的UnionBase类,以便您可以针对具有此类的程序集编译Main代码。比在运行时加载包含特定基类的程序集。

常见警告:你需要有很好的理由和理解才能走这条路。即现有的外部代码是考虑这种方法的一个原因。

“不要在家里做,由训练有素的专业人员在封闭的课程上进行”。

答案 2 :(得分:1)

可以使用基于编译时AOP的一些weaving解决方案实现另一个可能的解决方法,即PostSharp,它能够无缝地向现有方法注入新方法和接口类型以及修改(拦截)现有的类型。

答案 3 :(得分:1)

实际上有一个很好的理由可能需要交换基类。假设您要修改基类,但不要因为它在其他团队之间共享而不打扰当前的代码库。假设有10个派生类继承自base。您可以创建10个以上的自定义派生类来覆盖基类,但这是很多工作。这是你做的。问题的关键是创建一个接口和一个基本代理类。

class Program
{
    static void Main(string[] args)
    {
          IActionable action = new Derived<Base1>();
          action.open();
          action = new Derived<Base2>();
          action.open();

     }
}

 // Proxybase is a fake base class. ProxyBase will point to a real base1 or
 // base2
public class Derived<T>:ProxyBase,IActionable
{
    public Derived():base(typeof(T))    

    // the open function is not overriden in this case allowing
    // the base implementation to be used
}

// this looks like the real base class but it is a fake
// The proxy simply points to the implementation of base1 or base2 instead
public abstract class ProxyBase: IActionable
{
    IActionable obj;
    public ProxyBase(Type type,params object[] args)
    {
             obj = (IActionable)Activator.CreateInstance(type,args);   
    }

    public virtual void open()
    {
          obj.open();
    }
}

// notice base1 and base2 are NOT abstract in this case
// consider this the original implementation of the base class
public class Base1: IActionable
{
      public virtual void open()
      {
            Console.WriteLine("base1 open");
      }

}

// here base2 acquired the functionality of base1 and hides base1's open 
   function
// consider this implementation the new one to replace the original one
public class Base2: Base1, IActionable
{
     public new virtual void open()
     {
           Console.WriteLine("base2 open");
     }
}

public interface IActionable
{
    void open();
}

结果如下

base1 open
base2 open

更新: 虽然这个答案有效,但事实是继承引入了耦合,这使得这个练习充其量只是困难。此外,在实际情况中,您的要求可能会导致您希望从c#中无法实现的多个基类派生。如果要交换基类,最好使用桥接设计模式(这实际上完全避免了继承,从而避免了耦合)。

答案 4 :(得分:0)

最接近的可能是通过将至少一个定义为接口,然后从一个派生到另一个派生来从两种类型派生。

答案 5 :(得分:0)

我能想到的最接近的事情如下:

http://msdn.microsoft.com/en-us/library/dd264736.aspx

static void Main(string[] args)
{
    ExampleClass ec = new ExampleClass();
    // The following line causes a compiler error if exampleMethod1 has only
    // one parameter.
    //ec.exampleMethod1(10, 4);

    dynamic dynamic_ec = new ExampleClass();
    // The following line is not identified as an error by the
    // compiler, but it causes a run-time exception.
    dynamic_ec.exampleMethod1(10, 4);

    // The following calls also do not cause compiler errors, whether 
    // appropriate methods exist or not.
    dynamic_ec.someMethod("some argument", 7, null);
    dynamic_ec.nonexistentMethod();
}

class ExampleClass
{
    public ExampleClass() { }
    public ExampleClass(int v) { }

    public void exampleMethod1(int i) { }

    public void exampleMethod2(string str) { }
}

我不知道动态语言运行时是否可以执行您希望它执行的操作。

  

最接近你可能会得到   通过定义at来从两种类型派生   然后,至少有一个作为接口   铸造从一个铸造到另一个。

我必须同意,根据这个例子,这个建议会满足他想要做的事情,这也是一个更好的设计然后他真正想做的事情。