特性与方法注入

    好久没写文章了,主要是前面一段时间比较忙,一直没空学习和思考,好不容易找了个休息日,写一下Blog
    前几篇已经写到过特性,并且用它实现了一些东西。这次来谈一下一种比较怪异的特性的运用——方法注入。
    其实,说他怪异,稍微过了点,因为就连M$给大家的类库就用这种运用,举个例子TransactionAttribute,就是一个对事务相关方法的一个行为指导的注入。为什么这么说,TransactionAttribute可以定义强制开始一个新的事务或者使用已经存在的事务,或者定义事务的隔离级别。但是,特性本身什么事情也不能做,必须由别的类来读去这个特性,由别的类根据这个特性做某些事,TransactionAttribute为什么会起作用?原因是在事务处理时,处理函数会创建一个StackTrace,通过查找当前堆栈上的方法的TransactionAttribute,获得相关的事务信息,通过这些,事务处理函数就以期望的方式工作了。(因为特性标记在某个直接或间接调用到事务的方法上,所以执行事务的时候,这个方法一定在这个堆栈上)
    利用这个原理,可以做出一种类似的,利用特性注入方法的方式。这里就以最简单的验证参数为例:
    1、创建一个验证特性,作为所有验证特性的基类:
    public abstract class ValidateAttribute : Attribute
    
{
        
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();
        }


    }


    3、下一步,创建一个验证方法:
    public static class Validator
    
{
        
public static void Validate(object value)
        
{
            StackFrame sf 
= new StackFrame(1false);
            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;
            }
        }

    跑起来看看,看到了什么?对了ArgumentNullException的默认Message,Validator.Validate并没有任何显式的抛出这个异常,那么为什么会检测出这个空参数哪?
    原因在于Validator.Validate检查了当前调用堆栈中前一个方法(当前方法就是Validator.Validate自身),也就是调用Validator.Validate方法的方法,读去这个方法中所有继承自ValidateAttribute的特性,调用它们的Validate方法,例子中,只有一个NonNullValidate特性,它的Validate方法判断了传入的Value是否为空,如果未空就抛出异常。
    同样,如果一个简单的非空验证不够时,可能新写一个特性类,继承自ValidateAttribute,在需要的验证的方法上添加这个特性就完成了。
    这么做的好处是什么?
    很简单,代码看起来清楚,修改更简单。
    因为,不需要在代码中可以避免很多 “if什么什么 throw什么什么”,代码看起来就是,验证什么变量,然后用这个变量什么什么干活。当大家不关心如何验证时,就不会出现大段大段的验证代码,污染大家的视觉了,使要修改的代码更容易找到。反过来,如果关心验证,就看在这个方法上有些什么特性(当然特性的名字要取的好一点,弄个一串稀奇古怪的字符串,反而更看不懂),如果发现验证用错了,或者缺了什么验证,修改一下方法上的特性就可以了。
    另一方面,这样验证代码也可以重用起来,例如做了个英文大小写的验证特性,就可以直接在每一个需要这种验证的方法上添加这个特性,而不需要每个方法自己实现。
    凡事总是有两面的,说完优点,说说缺点,这个方式非常的灵活,但是代价是反射,在性能要求高于灵活要求的时候,这种方式就不适合了。

原文地址:https://www.cnblogs.com/vwxyzh/p/923293.html