一、何为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(ExcuteAttribute), 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(CustomFilterAttribute), 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
声明:本博客原创文字只代表本人工作中在某一时间内总结的观点或结论,与本人所在单位没有直接利益关系。非商业,未授权贴子请以现状保留,转载时必须保留此段声明, 且在文章页面明显位置给出原文连接。