ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2019-10-30 02:05:57  阅读:347  来源: 互联网

标签:inheritance virtual override c net-4-0


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

实例化派生类时.必须首先调用其所有基类的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();
    }
}

但是有一天,会发生一些不可思议的事情,使惯例变得混乱:

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

他们可能在尝试执行自己的操作之前调用base.TearDown(),或者根本不调用base方法,并造成一些严重损害.

因为我不信任抽象类的派生类遵循约定,并且他们可能选择从复杂性各异的任何基类派生,所以我唯一想到的选择是在每个新的基类中密封虚拟方法并公开一些新的抽象方法,在这种情况下,派生程序可以根据需要指定自己的操作:

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
    }
}

好吧,至少现在无论有人尝试从哪个类派生,他们都不可能弄乱SetUp和TearDown逻辑,但是这种模式很快就可以解决.

当只需要考虑一个继承级别时,这是一种经典模式,但是在我的情况下,我们可能会得到越来越复杂的类,这些类都依赖于维护SetUp和TearDown方法的顺序.

我是什么做的?

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

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

解决方法:

我想出了一种简洁的模式,该模式将在构造时注册的动作存储在有序列表中.

优点:

>保证安装和拆卸顺序
>实施其他设置和拆卸逻辑的清晰方法.
>一致的模式,无论继承了什么基类.

缺点:

>需要基本实例字段,因此在静态类需要此模式的情况下将不起作用. (幸运的是,这不是问题,因为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 }
}

使用任何基类的派生程序都可以自由使用它们的判断力,并具有执行某些任务的清晰方法,或者传入匿名委托,或者根本不定义自定义SetUp或TearDown操作:

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

标签:inheritance,virtual,override,c,net-4-0
来源: https://codeday.me/bug/20191030/1964506.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有