2013-05-13 77 views
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的引用类型已经处理完毕。
  • 确认在[finally]块中已经手动取消参考[System .__ ComObject]类型的所有参考类型并将其设置为[null]。

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

任何提示将不胜感激。

+3

只是一个想法:如果方法*需要*被覆盖,为什么他们是'虚拟'而不是'抽象'? – 2013-05-13 14:16:31

+0

更正了它。自从我在SO编辑器中写一个例子以来,我就错了。 – 2013-05-13 14:40:27

回答

6

的几点思考:

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

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

  • 为什么禁止实施的接口?
  • 为什么禁止宣告其他方法?
  • 为什么禁止catch子句?

你真的应该考虑一下你的实际业务需求是什么,在他们之后你的类模型。如果这些课程需要履行某个合同,则对该合同进行建模。将实现留给实现者。


关于提出的实际问题:
这里不能使用反射。您可以分析已编译程序集的原始源代码或IL代码。
这两个选项都非常棘手,而且在您有限的时间内很可能无法实现。我认为修复这个架构所需的时间比实现这些选项所需的时间要少。

+1

+为何拥有多达1亿个类名? :) – 2013-05-13 14:23:30

+0

我输入了一个简单的例子,所以有错误。虚拟固定。捕获块被删除。返回类型固定。当然,它适用于一次性使用。接下来,这个项目非常庞大而笨重,我们没有时间或预算来干预建筑。这些派生类的用途非常狭窄,我需要确保它符合这些标准。你的回答没有解决我的任何问题。 – 2013-05-13 14:47:09

+0

@RaheelKhan:为您的问题添加了明确的答案。 – 2013-05-13 14:53:54

0

虽然我敢肯定,你有一个很好的理由这样的刚性需求...你认为一个lambda的/代表/动作传递给测试功能呢?

能不能解决所有问题,但将更多的逻辑给你一些你想要的行为(例如不能返回,不能有一流水平的变量,任何地方,但规定不能写代码)。

最大的担心是捕获变量......但可能会有解决方法。

示例代码:

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

不知道这是否会工作,但它只是一个想法。噢,作为一个想法,它不是彻底的或测试 - 我希望你大大发展。

+0

谢谢。除非我错过了某些东西,否则你可能误解了这种情况。为许多类生成函数框架,然后个别开发人员填充它们。作为示例,'[ComObject com = null;]'不是模板的一部分。所以我不确定你的方法如何适合这里。 – 2013-05-13 17:34:21

+0

“所以我不确定你的方法如何适合这里。”不要试图通过坚持人们遵守任意规则来强制执行这种模式,您可以使用内置语言功能来为您执行这些限制。 Com对象可以在逻辑lambda中声明,在arg创建函数中创建,作为参数传入等。 – NPSF3000 2013-05-13 23:19:11

+0

嗯......你能指导我如何使用这些语言特性来执行这样的规则。 – 2013-05-13 23:32:10

2

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

+0

谢谢。这看起来很有趣。我正在探索CodeDom,但经过一番研究后,可能会与Roslyn一起工作。是的,这是很多工作,尽管我们已经有了模板生成和开发指南,但验证数以千计的细微模式偏差的类是一场噩梦,除非您有一定程度的自动化分析的信心。从那里开始,逻辑验证和测试只需要很少的时间。 – 2013-05-13 17:30:32