【基础】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(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

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

时间: 2024-11-05 20:33:26

【基础】Attribute的妙用的相关文章

C#基础---Attribute(标签) 和 reflect(反射) 应用二

以前我有写过一篇有关,打标签和反射的应用,主要用于类中字段的验证.下面是连接 C#基础---Attribute(标签) 和 reflect(反射) 应用. 这个项目迭代发现公司项目里面发现老代码对业务扩展比较大的地方又通过打标签和反射做了一个小的框架,感觉挺实用的.于是想一想模仿一下就搬到博客园上.写得可能不好,也请各位博友指点. 背景: [为了方便,自己还是模拟一个背景吧, 感觉不是很恰当,不过自己的Demo里面自己这样写的.当然跟公司的比起来肯定不够完善]   每个人都会吃饭,可是每个国家吃

C#基础--Attribute(标签) 和 reflect(反射) 应用

1.Attribute的定义与作用: 公共语言运行时允许你添加类似关键字的描述声明,叫做attributes, 它对程序中的元素进行标注,如类型.字段.方法和属性等.Attributes和Microsoft .NET Framework文件的元数据保存在一起,可以用来向运行时描述你的代码,或者在程序运行的时候影响应用程序的行为.比如在WCF中常用的序列化.比如添加的[DataMenber]就是一种标签.以及xml序列化也是一样的. 2.Reflect的定义与作用 反射可以用于观察并修改程序在运行

Rust 1.7.0 语法基础 attribute

一.语法 attribute : '#' '!' ? '[' meta_item ']' ; meta_item : ident [ '=' literal | '(' meta_seq ')' ] ? ; meta_seq : meta_item [ ',' meta_seq ] ? ; 二.说明 使用Java的人都知道,Java有个 annotation 注解. Rust 也有注释,名称和C#的注释一样,叫做 attribute. Rust attribute 用于在源代码中标识编译过程中的

Java基础之枚举妙用

对于枚举,初学Java的时候可能我们就已经接触过了,但是在毕业前,其实一直都不知道真正工作里面枚举是怎么用的,枚举有什么用?接下来,博主就介绍枚举在实际工作中的一种使用场景,本文只适合初级的小菜鸟看哈,大神看了可别嘲笑这是水文哦,哈哈! 一.使用场景 在实际的工作中,比如我有一个选择家庭关系的下拉选择框,大家都是会是value和desc的组合,一般我们保存到数据库的都是value(英文),然后用户看到的是desc(中文).但是我很多个页面会用到这样的下拉框,甚至可能很多个系统会用到,需要保持数据

C#基础笔记---浅谈XML读取以及简单的ORM实现

背景: 在开发ASP.NETMVC4 项目中,虽然web.config配置满足了大部分需求,不过对于某些特定业务,我们有时候需要添加新的配置文件来记录配置信息,那么XML文件配置无疑是我们选择的一个方案之一.下面简单谈谈XML的读取.  一. xml.linq读取xml 1.新建一个data.XML文件 1 <Customers> 2 <Customer> 3 <Name>Frank</Name> 4 <City>成都</City>

Rust 1.7.0 语法基础 标识符(ident)和分隔符的约束

一.标识符 identifier 是标识符,在Rust语法中简写为 ident . ident 是由任意个非空的unicode字符组成. 举例: 在 attribute 语法中,有 meta_item : ident [ '=' literal | '(' meta_seq ')' ] ? ; 实际使用如下: #![crate_type = "lib"] #![allow(dead_code)] #![feature(intrinsics, lang_items)] #[test] 上

java web 开发三剑客 -------电子书

Internet,人们通常称为因特网,是当今世界上覆盖面最大和应用最广泛的网络.根据英语构词法,Internet是Inter + net,Inter-作为前缀在英语中表示“在一起,交互”,由此可知Internet的目的是让各个net交互.所以,Internet实质上是将世界上各个国家.各个网络运营商的多个网络相互连接构成的一个全球范围内的统一网,使各个网络之间能够相互到达.各个国家和运营商构建网络采用的底层技术和实现可能各不相同,但只要采用统一的上层协议(TCP/IP)就可以通过Internet

C#基础系列:实现自己的ORM(反射以及Attribute在ORM中的应用)

反射以及Attribute在ORM中的应用 一. 反射什么是反射?简单点吧,反射就是在运行时动态获取对象信息的方法,比如运行时知道对象有哪些属性,方法,委托等等等等.反射有什么用呢?反射不但让你在运行是获取对象的信息,还提供运行时动态调用对象方法以及动态设置.获取属性等的能力.反射在ORM中有什么用呢?我这里所讨论的ORM实现是通过自定义Attribute的方式进行映射规则的描述的.但是我们并不知道具体哪个对象需要对应哪个表,并且这些对象是独立于我们的ORM框架的,所以我们只能通过自定义Attr

.NET基础编程之特性 - Attribute

这一篇文章是给大家介绍的是:.NET基础编程之特性 - Attribute,对这一部分掌握不熟悉的同学,可以仔细的看一下! 一.特性简介 特性提供功能强大的方法,用以将元数据或声明信息与代码(程序集.类型.方法.属性等)相关联.特性与程序实体关联后,可在运行时使用"反射"查询特性. 特性具有以下属性: (1)特性可向程序中添加元数据.元数据是有关在程序中定义的类型的信息.所有的 .NET 程序集都包含指定的一组元数据,这些元数据描述在程序集中定义的类型和类型成员.可以添加自定义特性,以