现在,Souceforge.net上面的AOP.NET增加了一个DBC(Design by contract)中提到的 前置/后置条件检查的功能.
现在相关的文件和dll可以下载了,地址在http://sourceforge.net/projects/aopnet/
现在要介绍的就是怎么样去使用这个ContractHandle.
先说明一下新增的几个类吧:
命名空间都为: NAop.Aspects.Contract
ContractAttribute : 从Attribute类直接继承,是一个Abstract Class,其中有一个Abstract的Check函数,用于ContractHandle进行统一调用.
MethodContractAttribute : 从ContractAttribute继承,同为Abstract Class,增加了AttributeTargets.Method 属性,使其派生类可以用于Method设置.
ParameterContractAttribute : 从ContractAttribute继承,同为Abstract Class,增加了AttributeTargets.Parameter | AttributeTargets.ReturnValue属性,使其派生类可以用于函数参数和返回值设置.
NotNullAttribute : 从MethodContractAttribute继承.从名字可以看出,是用来决定参数是否为null的.通过其构造函数,可以分别设置某些参数为null/不为null.
ExpectedTypeAttribute : 从ParameterContractAttribute继承. 用于检查实际的参数是否为期望的Type的实例,或是期望Type的子类的实例.
RangeAttribute : 从ParameterContractAttribute继承.用于对参数进行检查的.其中分为4类,分别为:
1,对Int,即数值型的参数进行检查.可以设置是否在某一范围间/外,是否属于/不属于集合.
2,对String类型.通过.NET自带的Regex(正则表达示类)来进行检查.可以设置RegexOption.
3,对Enum枚举型进行检查,可以设置是否属于/不属于某集合.
4,对其它的类型进行检查.通过设置一个Delegate,将实际的检查交由程序员自己实现..
RangeIntAttribute ,RangeStringAttribute ,RangeEnumAttribute, RangeOtherAttribute : 从RangeAttribute继承,由于RangeAttribute构造函数太多,通过这些子类进行分类.方便使用.
ContractHandle : 从IAsect 继承,这样就可以直接加入到AspectCollection中,通过AopBaseContext进行调用.
BeforeInvoke/AfterInvoke分别就是在函数调用前/后调用此函数,是由AopBaseContext来调用的.实际调用其函数/参数/返回值上的Attribute,再调用其Check函数,达到检查的目的.
注: 对于上述每个类的构造函数,都有一个参数(bool enable),用于设置是否取消DBC检查.
对于DBC中的Pre/PostCondition,默认情况下面是这样的,对于ref/out参数,是进行Post检查(即函数调用后),其他的参数/返回值,是进行Pre检查(函数调用前).
使用方法:
老样子,先通过AopFactory生成相应的Proxy,生成时的AopAspectCollection中增加上述ContractHandle类的实例.
再在每一个Virtual函数上加相应的Attribute.
运行,就OK了~
参考代码如下:
public enum EnumItems{
Item1,
Item2,
Item3,
}
public interface IContractSimp{}
public class ContractSimp : IContractSimp{
[Contract.NotNull]
public virtual object CreateNew(ContractSimp obj,int i){
if( i == 1)
{
return null;
}
return new ContractSimp();
}
[NotNull]
public virtual void CreateNew(ContractSimp obj1,ref ContractSimp obj2,out ContractSimp obj3)
{
obj2 = obj3 = null;
}
[Contract.NotNull("obj,obj2",NotNullType.ParamInclude)]
public virtual void CreateNew( ContractSimp obj,object obj1,string obj2)
{
}
[Contract.NotNull("obj,obj2",NotNullType.ParamExclude)]
public virtual void CreateNew3( ContractSimp obj,object obj1,string obj2)
{
}
public virtual void TestForString( [RangeString("http://([\\w-]+\\.)+[\\w-]+(/[\\w- ./?%&=]*)?")]string obj1){}
public virtual void TestForTypeEquals( [ExpectedType(typeof(IContractSimp),true)] object obj){
}
public virtual void TestForEnumRange( [RangeEnum("Item1,Item3",typeof(EnumItems),RangeType.EnumExclude)] EnumItems item){ }
public virtual void TestRangeInt([RangeInt(10,10,RangeType.IntBeyond)] int obj1,[RangeInt("12,48,58",RangeType.IntExclude)]int obj2){}
public virtual void TestOtehrRange( [RangeOther(typeof(ContractSimp),"DelegateCheck","123")]object obj){}
public static bool DelegateCheck(IAopContext context,int index,string attachinfo){ Console.WriteLine(attachinfo); return false;
}
}
/// <summary>
/// Summary description for TestContract.
/// </summary>
///
[TestFixture]
public class TestContract
{
private ContractSimp test;
[SetUp] public void Setup(){
AopAspectCollection aspects = new AopAspectCollection();
aspects.Add(new ContractHandle());
test = (ContractSimp)AopFactory.Create(typeof(ContractSimp),new object[0],aspects);
}
[Test,ExpectedException( typeof( ReturnNullException))] public void TestCreateNew(){
test.CreateNew( new ContractSimp(),1);
}
[Test,ExpectedException( typeof( ArgumentNullException))] public void TestCreateNew1(){
test.CreateNew( null,0);
}
[Test] public void TestCreateNew2(){
test.CreateNew( new ContractSimp(),null,"tiger");
}
[Test] public void TestCreateNew3(){
test.CreateNew3(null,new object(),null);
}
[Test,ExpectedException( typeof(ArgumentNullException))] public void TestCreateNewRef(){
ContractSimp obj3;
ContractSimp obj2 = new ContractSimp();
test.CreateNew(new ContractSimp(),ref obj2,out obj3);
}
[Test,ExpectedException( typeof( TypeNotMatchException))] public void TestTypeEquals(){
test.TestForTypeEquals( new ContractSimp());
test.TestForTypeEquals( new object());
}
[Test,ExpectedException( typeof( RangeNotMatchException))] public void TestForEnum(){
test.TestForEnumRange( EnumItems.Item2);
test.TestForEnumRange( EnumItems.Item1);
}
[Test,ExpectedException( typeof( RangeNotMatchException))] public void TestForRangeInt(){
test.TestRangeInt( 10,48);
}
[Test,ExpectedException( typeof( RangeNotMatchException))] public void TestRangeOther(){
test.TestOtehrRange( new object());
}
[Test] public void TestRangeString(){
test.TestForString("http://www.sina.com.cn");
}
}
注: 其类的名字参考了JGTM的一个评论.在此说明.非常感谢 :P
有什么问题,请与 hBifTs/dudu联系.