• Attribute的妙用 ---- 拦截器(过滤器)


    一、何为Attribute

    下面是微软官方对Attribute的解释:

    公共语言运行时允许你添加类似关键字的描述声明,叫做Attributes,它对程序中的元素进行标注,如类型、字段、方法和属性等。Attributes和Microsoft .NET Framework文件的元数据保存在一起,可以用来向运行时描述你的代码,或者在程序运行的时候影响应用程序的行为。

    通俗地理解,就是对目标对象(程序集、类、方法等)进行扩展,使得在运行时可以获取到被扩展对象的额外的信息,通过额外的信息来影响目标对象的行为。上面这句话纯粹是个人的理解,如有不妥希望指教。

    二、使用Attribute

    现在我有一个需求,创建一个包含 三个静态方法的类,如果某个方法被打上了标签,并且标签的Flag是1,那么就执行该方法,否则就不执行。看起来有点像过滤器,那么如何来实现这个小需求呢?首先要创建一个静态类MethodToRun,该类有三个静态方法分别是Run、Walk、Go,代码如下:

    复制代码
     1 public class MethodToRun
     2 {
     3     public static void Run ()
     4     {
     5         Console.WriteLine("Run Run Hurry Up!");
     6         Console.ReadLine();
     7     }
     8 
     9     public static void Walk()
    10     {
    11         Console.WriteLine("Walk Slowly~");
    12         Console.ReadLine();
    13     }
    14 
    15     public static void Go()
    16     {
    17         Console.WriteLine("Go Go Go!");
    18         Console.ReadLine();
    19     }
    20 }
    复制代码

    好了,有了以上的类,接下来开始创建我们自定义的Attribute,为了和Property属性做个区分,我称之为特性。取个名字叫ExcuteAttribute,拥有一个Flag属性,代码如下:

    1 [AttributeUsage(AttributeTargets.Method)]
    2 public class ExcuteAttribute : Attribute
    3 {
    4     public int Flag { get; set; }
    5 }

    上述代码第一行指定了该特性作用的范围,回头看下我们之前说的一句话:

    就是对目标对象(程序集、类、方法等)进行扩展,使得在运行时可以获取到被扩展对象的额外的信息,通过额外的信息来影响目标对象的行为。

    这里的AttributeUsage中的参数AttributeTargets就是目标对象,它是一个枚举类型,具体的枚举如下:

    复制代码
     1 //指定可以对它们应用属性的应用程序元素。
     2 [ComVisible(true)]
     3 [Flags]
     4 public enum AttributeTargets
     5 {
     6     //可以对程序集应用属性。
     7     Assembly = 1,
     8     
     9     //可以对模块应用属性。
    10     Module = 2,
    11     
    12     //可以对类应用属性。
    13     Class = 4,
    14     
    15     //可以对结构应用属性,即值类型。
    16     Struct = 8,
    17     
    18     //可以对枚举应用属性。
    19     Enum = 16,
    20     
    21     //可以对构造函数应用属性。
    22     Constructor = 32,
    23      
    24     //可以对方法应用属性。
    25     Method = 64,
    26      
    27     //可以对属性 (Property) 应用属性 (Attribute)。
    28     Property = 128,
    29      
    30     //可以对字段应用属性。
    31     Field = 256,
    32      
    33     //可以对事件应用属性。
    34     Event = 512,
    35      
    36     //可以对接口应用属性。
    37     Interface = 1024,
    38      
    39     //可以对参数应用属性。
    40     Parameter = 2048,
    41      
    42     //可以对委托应用属性。
    43     Delegate = 4096,
    44      
    45     //可以对返回值应用属性。
    46     ReturnValue = 8192,
    47      
    48     //可以对泛型参数应用属性。
    49     GenericParameter = 16384,
    50     
    51     //可以对任何应用程序元素应用属性。
    52     All = 32767
    53 }
    复制代码

    标签创建完成了,我们修改一下MethodToRun这个类,加上标签,代码如下:

    复制代码
    public class MethodToRun
    {
        [Excute(Flag =1)]
        public static void Run ()
        {
            Console.WriteLine("Run Run Hurry Up!");
            Console.ReadLine();
        }
    
        public static void Walk()
        {
            Console.WriteLine("Walk Slowly~");
            Console.ReadLine();
        }
    
        [Excute(Flag =2)]
        public static void Go()
        {
            Console.WriteLine("Go Go Go!");
            Console.ReadLine();
        }
    }
    复制代码

    其中,Run方法和Go方法加上了标签,且Flag的值分别为1和2,我们的需求是贴了标签并且Flag为1的方法被执行,下面来看下关键的调用代码:

    复制代码
     1 class Program
     2 {
     3     static void Main(string[] args)
     4     {
     5         //获取MethodToRun类的静态方法集合
     6         MethodInfo[] methods = typeof(MethodToRun).GetMethods(BindingFlags.Public|BindingFlags.Static);
     7         foreach (var method in methods)
     8         {
     9             MethodInfo info = method;
    10             //获取每个方法上的Attributes集合
    11             var attributes = info.GetCustomAttributes(typeof(Attribute), false);
    12             
    13             foreach (var attri in attributes)
    14             {
    15                 //如果自定义的标签是指定的标签则符合条件
    16                 if(attri is ExcuteAttribute)
    17                 {
    18                     ExcuteAttribute exe = attri as ExcuteAttribute;
    19                     //执行Flag为1的方法
    20                     if(exe.Flag == 1)
    21                     {
    22                          info.Invoke(null, null);
    23                     }
    24                 }
    25             }
    26         }
    27         Console.ReadLine();
    28     }
    29 }
    复制代码

    运行结果:

    整个过程就是通过反射获取目标集合,本例子中的MethodToRun类中的静态方法(因为标签都贴在了静态方法中,且AttributeTargets指定的目标也是静态方法),然后获取方法上的自定义标签集合,相当于方法的额外的信息,通过这些额外的信息来影响方法的执行,现在再回头看看第一小节的粗体部分:

    就是对目标对象(程序集、类、方法等)进行扩展,使得在运行时可以获取到被扩展对象的额外的信息,通过额外的信息来影响目标对象的行为。

     以上是个人比较浅薄的理解,如果您有更深层次的理解,欢迎讨论,互相学习。

     三、自己写拦截器

    根据上面的表述,结合ASP.NET MVC常用的拦截器功能,自己实现一个极简的拦截器。首先定义一个接口ICustomFilter,包含两个接口方法,OnBeforeAction和OnAfterAction,代码如下:

    复制代码
     1 /// <summary>
     2 /// 自定义拦截器
     3 /// </summary>
     4 public interface ICustomFilter
     5 {
     6     //Action执行之前执行
     7     void OnBeforeAction();
     8     
     9     //Action执行之后执行
    10     void OnAfterAction();
    11 }
    复制代码

    再定义一个Attribute实现该接口:

    复制代码
     1 public class CustomFilterAttribute : Attribute, ICustomFilter
     2 {
     3     public void OnAfterAction()
     4     {
     5         Console.WriteLine("Action 执行之后进行拦截!");
     6     }
     7 
     8     public void OnBeforeAction()
     9     {
    10         Console.WriteLine("Action 执行之前进行拦截!");
    11     }
    12 }
    复制代码

    准备工作完成了,接下来给目标方法打上标签,修改下MethodToRun中的代码:

    复制代码
    public class MethodToRun
    {
        [Excute(Flag =1)]
        public static void Run ()
        {
            Console.WriteLine("Run Run Hurry Up!");
            Console.ReadLine();
        }
    
        [CustomFilter]
        public static void Walk()
        {
            Console.WriteLine("Walk Slowly~");
            Console.ReadLine();
        }
    
        [Excute(Flag =2)]
        public static void Go()
        {
            Console.WriteLine("Go Go Go!");
            Console.ReadLine();
        }
    }
    复制代码

    其中Walk方法添加了[CustomAttribute]特性,接下来看下调用代码是如何实现在Walk执行前后分别执行OnBeforeAction和OnAfterAction方法的,代码如下:

    复制代码
    class Program
    {
        static void Main(string[] args)
        {
            MethodInfo[] methods = typeof(MethodToRun).GetMethods(BindingFlags.Public|BindingFlags.Static);
            foreach (var method in methods)
            {
                MethodInfo info = method;
                var attributes = info.GetCustomAttributes(typeof(Attribute), false);
                
                foreach (var attri in attributes)
                {
                    if(attri is ExcuteAttribute)
                    {
                        ExcuteAttribute exe = attri as ExcuteAttribute;
                        if(exe.Flag == 1)
                        {
                            //info.Invoke(null, null);
                        }
                    }
                    //这里是重点
                    if(attri is CustomFilterAttribute)
                    {
                        CustomFilterAttribute cust = attri as CustomFilterAttribute;
                        cust.OnBeforeAction();
                        info.Invoke(null, null);
                        cust.OnAfterAction();
                    }
                }
            }
            Console.ReadLine();
        }
    }
    复制代码

    现在跑一下看看效果

    上述小例子只是一个小小的应用,在.Net体系中,Attribute随处可见,其应用范围十分广泛。

    出处:http://www.cnblogs.com/xhb-bky-blog/p/7840265.html

  • 相关阅读:
    模仿微信右上角菜单栏
    改变checkbox的默认样式
    webpack中的静态资源处理
    如何解决Unsupported Architecture. Your executable contains unsupported architecture '[x86_64, i386]
    页面生命周期
    关于在使用Exchange2003系统时无法向sina,yahoo,hotmail等邮箱发送邮件问题的解决方法
    重置TCP/IP协议堆栈的经历
    网通电信双线路上网,网通的走网通线路,电信的走电信线路,内网通过NAT上网,双线路故障自动切换
    在OUTLOOK或OWA中查看邮件的SCL级别(转)
    常用的RBL服务器列表及介绍
  • 原文地址:https://www.cnblogs.com/mq0036/p/7844369.html
Copyright © 2020-2023  润新知