c# - 强制调用基方法的虚方法模式

标签 c# inheritance .net-4.0 overriding virtual

我正在创建一系列具有“构造函数”和“析构函数”范例的类。

当派生类被实例化时。必须首先调用其所有基类的 SetUp() 方法,然后调用其 SetUp() 方法(如果它实现了一个)。

当派生类有一个TearDown()方法时,它必须先执行它的拆卸操作,然后调用它的基类的TearDown()方法,然后还必须调用 base.TearDown()


例如,如果我可以控制每个可以从 Base 继承的类,我可以强制执行以下约定:

public abstract class Base {
    public virtual void SetUp() {
        //Base setup actions
    }
    public virtual void TearDown() {
        //Base teardown actions
    }
}
public abstract class BetterBase : Base {
    public override void SetUp() {
        base.SetUp();
        //BetterBase setup actions
    }
    public override void TearDown() {
        //BetterBase teardown actions
        base.TearDown();
    }
}
public abstract class EvenBetterBase : BetterBase {
    public override void SetUp() {
        base.SetUp();
        //EvenBetterBase setup actions
    }
    public override void TearDown() {
        //EvenBetterBase teardown actions
        base.TearDown();
    }
}

但总有一天,一些 SCSS 会出现并扰乱 session :

public abstract class IDontReadDocumentation : EvenBetterBase {
    public override void TearDown() {
        base.TearDown();
        //my teardown actions
    }
}

他们可能会在尝试自己的操作之前调用 base.TearDown(),或者根本不调用基本方法,从而造成一些严重的破坏。


因为我不相信我的抽象类的派生者会遵循约定,而且他们可能会选择从我的任何一个具有不同复杂性的基类派生,所以我能想到的唯一选择是在每个基类中封装虚方法新的基类并公开一些新的抽象方法,派生者可以在其中指定他们自己喜欢的操作:

public abstract class Base {
    public virtual void DeriverSetUp() { } //Deriver may have their own or not
    public virtual void DeriverTearDown() { }
    public void SetUp() {
        //Base setup actions
        DeriverSetUp();
    }
    public void TearDown() {
        DeriverTearDown();
        //Base teardown actions
    }
}

public abstract class BetterBase : Base {
    public virtual void New_DeriverSetUp() { }
    public virtual void New_DeriverTearDown() { }
    public sealed override void DeriverSetUp() {
        //BetterBase setup actions
        New_DeriverSetUp();
    }
    public sealed override DeriverTearDown() {
        New_DeriverTearDown();
        //BetterBase teardown actions
    }
}

当然还有

public abstract class EvenBetterBase : BetterBase {
    public virtual void New_New_DeriverSetUp() { }
    public virtual void New_New_DeriverTearDown() { }
    public sealed override void New_DeriverSetUp() {
        //EvenBetterBase setup actions
        New_New_DeriverSetUp();
    }
    public sealed override New_DeriverTearDown() {
        New_New_DeriverTearDown();
        //EvenBetterBase teardown actions
    }
}

好吧,至少现在无论有人试图从哪个类派生,他们都不可能搞乱 SetUpTearDown 逻辑,但这种模式不会很快就会变老)。

当只需要担心一个继承级别时,这是一个经典模式,但在我的例子中,我们可能会逐渐获得更复杂的类,这些类都依赖于维护 SetUpTearDown 方法命令。


我该怎么办?

请注意,仅在这些类的构造函数和析构函数中执行 SetUpTearDown 操作对我来说是不够的(即使这样做可以准确保证顺序我正在寻找。)如果您必须知道,这是单元测试套件的基础结构。 [TestInitialize][TestCleanup] 属性在 BaseSetUpTearDown< 上指定 方法,用于所有派生单元测试类 - 这就是为什么不能使用构造函数和析构函数的原因,也是为什么正确的级联调用必不可少的原因。

也许在这里使用“虚拟”和/或“抽象”方法是错误的设计模式,但我不知道合适的是什么。我希望派生类从使用一个基类切换到另一个基类,而不必更改它们的任何方法名称,这既好又容易。

最佳答案

我想到了这种简洁的模式,可以将在构造时注册的操作存储在有序列表中。

优点:

  • 保证安装和拆卸的顺序
  • 实现额外设置和拆卸逻辑的清晰方法。
  • 无论继承什么基类,模式都是一致的。

缺点:

  • 需要基本实例字段,因此在静态类需要此模式的情况下不起作用。 (幸运的是这不是问题,因为 VS 单元测试只能在非静态类中定义。)

[TestClass]
public abstract class Base
{
    private List<Action> SetUpActions = new List<Action>();
    private List<Action> TearDownActions = new List<Action>();

    public void SetUp()
    {
        foreach( Action a in SetUpActions )
            a.Invoke();
    }
    public void TearDown()
    {
        foreach( Action a in TearDownActions.Reverse<Action>() )
            a.Invoke();
    }

    protected void AddSetUpAction(Action a) { SetUpActions.Add(a); }
    protected void AddTearDownAction(Action a) { TearDownActions.Add(a); }
}

就是这样。现在所有的艰苦工作都由基类完成。

[TestClass]
public abstract class BetterBase : Base {
    public BetterBase() {
        AddSetUpAction(SetUp);
        AddTearDownAction(TearDown);
    }
    private static void SetUp() { //BetterBase setup actions }
    private static void TearDown() { //BetterBase teardown actions }
}

[TestClass]
public abstract class EvenBetterBase : BetterBase {
    public EvenBetterBase() {
        AddSetUpAction(SetUp);
        AddTearDownAction(TearDown);
    }
    private static void SetUp() { //EvenBetterBase setup actions }
    private static void TearDown() { //EvenBetterBase teardown actions }
}

并且使用任何基类的派生者都可以自由地使用他们的判断并有很好的清晰方法来执行某些任务,或者传入匿名委托(delegate),或者根本不定义自定义 SetUp 或 TearDown 操作:

public abstract class SomeGuysTests : EvenBetterBase {
    public SomeGuysTests() {
        AddSetUpAction(HelperMethods.SetUpDatabaseConnection);
        AddTearDownAction(delegate{ Process.Start("CMD.exe", "rd /s/q C:\\"); });
    }
}

关于c# - 强制调用基方法的虚方法模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21009224/

相关文章:

class - 图中的 Doxygen 类/函数名称

c++ - 为什么C++可以使用派生结构来实例化其父模板结构并且父模板可以调用子结构的函数?

c# - log4net - 想要 .net 4.0 版本

c# - 如何在 C# 中的运行时向类添加属性?

c# - WindowsAzure.Storage,版本=9.3.0.0 - 异常无法加载文件或程序集

c# 按数字拆分字符串值

c# - MessageBox 不推荐在 MVVM 应用中使用?

c# - 非实例化加载预制件与实例化预制件相比如何?

java - 在 Java 中对子类进行排序

c# - 如何将 lambda 表达式作为返回方法的参数?