0

我正在从事一个大型项目,其中一个基类有数千个派生自它的类(多个开发人员正在研究它们)。每个类都应覆盖一组方法。我首先使用符合可接受模式的代码模板生成了这数千个类文件。我现在正在编写单元测试以确保开发人员没有偏离这种模式。这是一个示例生成的类:

// Base class.
public abstract partial class BaseClass
{
    protected abstract bool OnTest ();
}

// Derived class. DO NOT CHANGE THE CLASS NAME!
public sealed partial class DerivedClass_00000001: BaseClass
{
    /// <summary>
    /// Do not modify the code template in any way.
    /// Write code only in the try and finally blocks in this method.
    /// </summary>
    protected override void OnTest ()
    {
        bool result = false;
        ComObject com = null;
        // Declare ALL value and reference type variables here. NOWHERE ELSE!
        // Variables that would otherwise be narrowly scoped should also be declared here.
        // Initialize all reference types to [null]. [object o;] does not conform. [object o = null;] conforms.
        // Initialize all value types to their default values. [int x;] does not conform. [int x = 0;] conforms.

        try
        {
            com = new ComObject();

            // Process COM objects here.
            // Do NOT return out of this function yourself!
        }
        finally
        {
            // Release all COM objects.
            System.Runtime.InteropServices.Marshal.ReleaseComObject(com);

            // Set all COM objects to [null].
            // The base class will take care of explicit garbage collection.
            com = null;
        }

        return (result);
    }
}

在单元测试中,我已经能够通过反射验证以下内容:

  • 该类派生自 [BaseClass] 并且不实现任何接口。
  • 类名符合模式。
  • catch 块没有被过滤。
  • 没有添加其他 catch 块。
  • 未声明任何类级别字段或属性。
  • 所有方法值类型变量均已在声明时手动初始化。
  • 没有其他方法被添加到派生类中。

以上通过反射很容易实现,但我正在努力断言以下列表:

  • catch 块重新抛出捕获的异常,而不是包装它或抛出一些其他异常。
  • [return (result);]末尾的行没有被修改,也没有[return (whatever);]添加其他调用。不知道如何实现这一目标。
  • 验证所有实现 IDisposable 的引用类型是否已被释放。
  • 验证 [System.__ComObject] 类型的所有引用类型是否已手动取消引用并在 finally 块中设置为 [null]。

我曾考虑过解析源代码,但除非绝对必要,否则我不喜欢这种解决方案。它很混乱,除非我有表达式树,否则几乎不可能保证成功。

任何提示将不胜感激。

4

3 回答 3

6

一些想法:

  1. 如果需要重写方法,为什么要使用它们virtual而不是abstract
  2. 不应更改的代码不属于派生类。它属于基类。
  3. catch { throw; }没用。去掉它。
  4. 从方法返回布尔值void会导致编译器错误。
  5. 将局部变量设置为null是没用的。
  6. 并非所有引用类型都实现IDisposable.

一般来说:您的大多数需求似乎没有商业价值。

  • 为什么禁止实现接口?
  • 为什么禁止声明其他方法?
  • 为什么要禁止catch条款?
  • 等等

您应该真正考虑一下您的实际业务需求是什么,并在它们之后为您的类建模。如果类需要履行某个契约,则对该契约建模。将实现留给实现者。


关于提出的实际问题:
您不能在这里使用反射。您可以分析原始源代码或已编译程序集的 IL 代码。
这两种选择都非常棘手,很可能无法在有限的时间内实现。我很肯定,与实施其中一个选项相比,修复架构所需的时间更少。

于 2013-05-13T14:18:50.820 回答
2

如果您真正需要全自动代码分析,您可以尝试在此处使用Roslyn CTP 。它比反射具有更高级的语法和语义分析。但这仍然是很多工作。直接与开发人员合作,而不是与他们的代码合作,准备模板,指南可能会更省时。

于 2013-05-13T16:10:04.190 回答
0

虽然我确信您对这种严格的要求有充分的理由......您是否考虑过将 Lambda/Delegates/Action 传递给 Test 函数?

不能解决所有问题,但更合乎逻辑地会给你一些你想要的行为(例如不能返回,不能有类级变量,不能在任何地方编写代码,但指定)。

对它最大的担忧是捕获的变量......但可能有解决方法。

示例代码:

//I'd make a few signatures....
bool OnTest<T1, T2> (Action<ComObject, T1, T2> logic, T1 first, T2 second)
    {
        bool result = false;
        ComObject com = null;

        //no checks needed re parameters
        //Can add reflection tests here if wanted before code is run.

        try
        {
            com = new ComObject();
            //can't return
            logic(com, first,second);
        }
        finally
        {
            // Release all COM objects.
            System.Runtime.InteropServices.Marshal.ReleaseComObject(com);

            // Set all COM objects to [null].
            // The base class will take care of explicit garbage collection.
            com = null;

            //If you want, we can check each argument and if it is disposable dispose.
            if (first is IDisposable  && first != null) ((IDisposable) first).Dispose();
            ...
        }

        return (result);  //can't be changed
    }

不知道这是否会起作用,但这只是一个想法。哦,作为一个想法,它没有彻底或经过测试 - 我希望你能彻底开发它。

于 2013-05-13T15:07:19.520 回答