好久没写文章了,主要是前面一段时间比较忙,一直没空学习和思考,好不容易找了个休息日,写一下Blog
前几篇已经写到过特性,并且用它实现了一些东西。这次来谈一下一种比较怪异的特性的运用——方法注入。
其实,说他怪异,稍微过了点,因为就连M$给大家的类库就用这种运用,举个例子TransactionAttribute,就是一个对事务相关方法的一个行为指导的注入。为什么这么说,TransactionAttribute可以定义强制开始一个新的事务或者使用已经存在的事务,或者定义事务的隔离级别。但是,特性本身什么事情也不能做,必须由别的类来读去这个特性,由别的类根据这个特性做某些事,TransactionAttribute为什么会起作用?原因是在事务处理时,处理函数会创建一个StackTrace,通过查找当前堆栈上的方法的TransactionAttribute,获得相关的事务信息,通过这些,事务处理函数就以期望的方式工作了。(因为特性标记在某个直接或间接调用到事务的方法上,所以执行事务的时候,这个方法一定在这个堆栈上)
利用这个原理,可以做出一种类似的,利用特性注入方法的方式。这里就以最简单的验证参数为例:
1、创建一个验证特性,作为所有验证特性的基类:
2、创建一个过滤空引用的验证特性:
3、下一步,创建一个验证方法:
4、用起来看看吧
跑起来看看,看到了什么?对了ArgumentNullException的默认Message,Validator.Validate并没有任何显式的抛出这个异常,那么为什么会检测出这个空参数哪?
原因在于Validator.Validate检查了当前调用堆栈中前一个方法(当前方法就是Validator.Validate自身),也就是调用Validator.Validate方法的方法,读去这个方法中所有继承自ValidateAttribute的特性,调用它们的Validate方法,例子中,只有一个NonNullValidate特性,它的Validate方法判断了传入的Value是否为空,如果未空就抛出异常。
同样,如果一个简单的非空验证不够时,可能新写一个特性类,继承自ValidateAttribute,在需要的验证的方法上添加这个特性就完成了。
这么做的好处是什么?
很简单,代码看起来清楚,修改更简单。
因为,不需要在代码中可以避免很多 “if什么什么 throw什么什么”,代码看起来就是,验证什么变量,然后用这个变量什么什么干活。当大家不关心如何验证时,就不会出现大段大段的验证代码,污染大家的视觉了,使要修改的代码更容易找到。反过来,如果关心验证,就看在这个方法上有些什么特性(当然特性的名字要取的好一点,弄个一串稀奇古怪的字符串,反而更看不懂),如果发现验证用错了,或者缺了什么验证,修改一下方法上的特性就可以了。
另一方面,这样验证代码也可以重用起来,例如做了个英文大小写的验证特性,就可以直接在每一个需要这种验证的方法上添加这个特性,而不需要每个方法自己实现。
凡事总是有两面的,说完优点,说说缺点,这个方式非常的灵活,但是代价是反射,在性能要求高于灵活要求的时候,这种方式就不适合了。
前几篇已经写到过特性,并且用它实现了一些东西。这次来谈一下一种比较怪异的特性的运用——方法注入。
其实,说他怪异,稍微过了点,因为就连M$给大家的类库就用这种运用,举个例子TransactionAttribute,就是一个对事务相关方法的一个行为指导的注入。为什么这么说,TransactionAttribute可以定义强制开始一个新的事务或者使用已经存在的事务,或者定义事务的隔离级别。但是,特性本身什么事情也不能做,必须由别的类来读去这个特性,由别的类根据这个特性做某些事,TransactionAttribute为什么会起作用?原因是在事务处理时,处理函数会创建一个StackTrace,通过查找当前堆栈上的方法的TransactionAttribute,获得相关的事务信息,通过这些,事务处理函数就以期望的方式工作了。(因为特性标记在某个直接或间接调用到事务的方法上,所以执行事务的时候,这个方法一定在这个堆栈上)
利用这个原理,可以做出一种类似的,利用特性注入方法的方式。这里就以最简单的验证参数为例:
1、创建一个验证特性,作为所有验证特性的基类:
public abstract class ValidateAttribute : Attribute
{
protected ValidateAttribute() { }
public abstract void Validate(object value);
}
{
protected ValidateAttribute() { }
public abstract void Validate(object value);
}
2、创建一个过滤空引用的验证特性:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public sealed class NonNullValidateAttribute
: ValidateAttribute
{
public NonNullValidateAttribute()
: base() { }
public override void Validate(object value)
{
if (value == null)
throw new ArgumentNullException();
}
}
public sealed class NonNullValidateAttribute
: ValidateAttribute
{
public NonNullValidateAttribute()
: base() { }
public override void Validate(object value)
{
if (value == null)
throw new ArgumentNullException();
}
}
3、下一步,创建一个验证方法:
public static class Validator
{
public static void Validate(object value)
{
StackFrame sf = new StackFrame(1, false);
MethodBase m = sf.GetMethod();
object[] attrs = m.GetCustomAttributes(typeof(ValidateAttribute), false);
foreach (ValidateAttribute attr in attrs)
{
attr.Validate(value);
}
}
}
{
public static void Validate(object value)
{
StackFrame sf = new StackFrame(1, false);
MethodBase m = sf.GetMethod();
object[] attrs = m.GetCustomAttributes(typeof(ValidateAttribute), false);
foreach (ValidateAttribute attr in attrs)
{
attr.Validate(value);
}
}
}
4、用起来看看吧
static object _testField;
static void Main(string[] args)
{
try
{
TestProp = null;
Console.WriteLine(TestProp);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
static object TestProp
{
get { return _testField; }
[NonNullValidate()]
set
{
Validator.Validate(value);
_testField = value;
}
}
static void Main(string[] args)
{
try
{
TestProp = null;
Console.WriteLine(TestProp);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
static object TestProp
{
get { return _testField; }
[NonNullValidate()]
set
{
Validator.Validate(value);
_testField = value;
}
}
跑起来看看,看到了什么?对了ArgumentNullException的默认Message,Validator.Validate并没有任何显式的抛出这个异常,那么为什么会检测出这个空参数哪?
原因在于Validator.Validate检查了当前调用堆栈中前一个方法(当前方法就是Validator.Validate自身),也就是调用Validator.Validate方法的方法,读去这个方法中所有继承自ValidateAttribute的特性,调用它们的Validate方法,例子中,只有一个NonNullValidate特性,它的Validate方法判断了传入的Value是否为空,如果未空就抛出异常。
同样,如果一个简单的非空验证不够时,可能新写一个特性类,继承自ValidateAttribute,在需要的验证的方法上添加这个特性就完成了。
这么做的好处是什么?
很简单,代码看起来清楚,修改更简单。
因为,不需要在代码中可以避免很多 “if什么什么 throw什么什么”,代码看起来就是,验证什么变量,然后用这个变量什么什么干活。当大家不关心如何验证时,就不会出现大段大段的验证代码,污染大家的视觉了,使要修改的代码更容易找到。反过来,如果关心验证,就看在这个方法上有些什么特性(当然特性的名字要取的好一点,弄个一串稀奇古怪的字符串,反而更看不懂),如果发现验证用错了,或者缺了什么验证,修改一下方法上的特性就可以了。
另一方面,这样验证代码也可以重用起来,例如做了个英文大小写的验证特性,就可以直接在每一个需要这种验证的方法上添加这个特性,而不需要每个方法自己实现。
凡事总是有两面的,说完优点,说说缺点,这个方式非常的灵活,但是代价是反射,在性能要求高于灵活要求的时候,这种方式就不适合了。