• 特性与方法注入


        好久没写文章了,主要是前面一段时间比较忙,一直没空学习和思考,好不容易找了个休息日,写一下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什么什么”,代码看起来就是,验证什么变量,然后用这个变量什么什么干活。当大家不关心如何验证时,就不会出现大段大段的验证代码,污染大家的视觉了,使要修改的代码更容易找到。反过来,如果关心验证,就看在这个方法上有些什么特性(当然特性的名字要取的好一点,弄个一串稀奇古怪的字符串,反而更看不懂),如果发现验证用错了,或者缺了什么验证,修改一下方法上的特性就可以了。
        另一方面,这样验证代码也可以重用起来,例如做了个英文大小写的验证特性,就可以直接在每一个需要这种验证的方法上添加这个特性,而不需要每个方法自己实现。
        凡事总是有两面的,说完优点,说说缺点,这个方式非常的灵活,但是代价是反射,在性能要求高于灵活要求的时候,这种方式就不适合了。

  • 相关阅读:
    linux常用小技巧(持续更新中)
    【CodeBase】PHP将数组键名转成变量名
    【Mysql】给mysql配置远程登录
    【Codebase】JQuery获取表单部分数据提交方法
    【shopex】添加网页挂件widgets
    【shopex】真正可用的app开发机制
    【转】Mysql查询语句优化策略
    【Ecshop】修改处理用户购物车的行为
    【Ecshop】商品数据采集扩展
    supervisor 使用
  • 原文地址:https://www.cnblogs.com/vwxyzh/p/923293.html
Copyright © 2020-2023  润新知