C#中的Attribute详解(上)

本文介绍了C#中的Attribute概念,解释了Attribute如何作为修饰符为元数据添加内容,并通过实例展示了Attribute与注释的区别及其在程序调试与诊断中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

近期正在研究AOP,本以为学会之后就又得了一个宝贝,想想心中还挺高兴的。我在学习时无意中发现了一位大牛在12年前写的一篇关于AOP的博客(http://www.cnblogs.com/wayfarer/articles/241024.html),写的真是很深入很不错,这时我突然感觉自己很渺小很无知,人家12年前就了如指掌的东西,我到如今还在东拼西凑地找学习资料,觉得自己真是太差劲了。不过后来我也想通了,闻道有先后,术业有专攻,只要自己不断地努力,学习并快乐着,掌握的知识越来越丰富,技艺越来越精湛,那自己的结果也不会差,有点扯多了……
那AOP跟这里讲的Attribute有什么关系呢?AOP中的动态代理就是利用Attribute实现的,所以要想搞清楚动态代理,先要弄明白Attribute,因此就有了这篇文章。

一、Attribute是什么

Attribute是一种可由用户自有定义的修饰符(Modifier),可以用来修饰各种需要被修饰的目标,修饰符(比如private、public、static、override、virtual等等)是C#语言本身的关键字。
简单地说,Attribute就是一种“附着物”——就像牡蛎吸附在船底或礁石上一样。
这些附着物的作用是为它们的附着体追加上一些额外的信息(这些信息保存在附着物的体内)——比如“这个类是我写的”或者“这个函数以前出过问题”等等。

二、Attribute的作用

Attribute的作用是为元数据添加内容。
元数据可以被工具支持,比如:编译器用元数据来辅助编译,调试器用元数据来调试程序。

三、Attribute与注释的区别

注释是对程序源代码的一种说明,主要目的是给人看的,在程序被编译的时候会被编译器所丢弃,因此,它丝毫不会影响到程序的执行。
Attribute是程序代码的一部分,它不但不会被编译器丢弃,而且还会被编译器编译进程序集(Assembly)的元数据(Metadata)里。在程序运行的时候,你随时可以从元数据中提取提取出这些附加信息,并以之决策程序的运行。

四、系统Attribute范例

在项目中,某个类由两个程序员(小张和小李)共同维护。这个类起到了“工具包”(Utilities)的作用,里面包含几十个静态方法,就像.Net Framework中的Math类一样。这些静态方法,一半是小张写的、一半是小李写的;在项目的测试中,有一些静态方法曾经出过bug,后来又被修正。
我们可以把这些方法分成这样几类:
这里写图片描述
我们分类的目的主要是在测试的时候可以按照不同的类别进行测试、获取不同的效果。比如:统计两个人的工作量或者对曾经出过bug的方法进行回归测试。

1、如果不使用Attribute,为了区分这四类静态方法,我们只能通过注释来说明,但这样做会给系统带来很多垃圾信息,而且不利于测试。注释区分法如下:

public static void Li_Buged_FuncA(){ }
public static void Li_NoBug_FuncB(){ }
public static void Zhang_Buged_FuncC(){ }
public static void Zhang_NoBug_FuncD(){ }

或

//Created by Li,Buged
public static void FuncA(){ }
//Created by Li,NoBug
public static void FuncB(){ }
//Created by Zhang,Buged
public static void FuncC(){ }
//Created by Zhang,NoBug
public static void FuncD(){ }

2、如果使用Attribute,区分这四类静态方法将会简单许多。当类Program和类ToolKit都在Program.cs文件中时,#define Buged只需要在Program.cs文件头部定义,示例代码如下:

#define Buged //C#的宏定义必须在所有代码之前。当前只让Buged宏有效
//#define NoBug
//#define Li
//#define Zhang
using System;
using System.Diagnostics;//注意:ConditionalAttribute特性包含在此命名空间中

namespace AttributeTest
{
    class Program
    {
        static void Main(string[] args)
        {
            ToolKit.FuncA();
            ToolKit.FuncB();
            ToolKit.FuncC();
            ToolKit.FuncD();
            Console.ReadKey();
        }
    }

    public class ToolKit
    {
        [ConditionalAttribute("Li")]//Attribute名称的长记法
        [ConditionalAttribute("Buged")]
        public static void FuncA()
        {
            Console.WriteLine("Created by Li, Buged");
        }

        [Conditional("Li")]//Attribute名称的短记法
        [Conditional("NoBug")]
        public static void FuncB()
        {
            Console.WriteLine("Created by Li, NoBug");
        }

        [Conditional("Zhang")]
        [Conditional("Buged")]
        public static void FuncC()
        {
            Console.WriteLine("Created by Zhang, Buged");
        }

        [Conditional("Zhang")]
        [Conditional("NoBug")]
        public static void FuncD()
        {
            Console.WriteLine("Created by Zhang, NoBug");
        }
    }
}

当类Program在Program.cs文件中,类ToolKit在ToolKit.cs文件中时,#define Buged需要在Program.cs文件头部和ToolKit.cs文件头部均定义,示例代码如下:

#define Buged //C#的宏定义必须在所有代码之前。当前只让Buged宏有效
//#define NoBug
//#define Li
//#define Zhang
using System;
using System.Diagnostics;
namespace AttributeTest
{
    class Program
    {
        static void Main(string[] args)
        {
            ToolKit.FuncA();
            ToolKit.FuncB();
            ToolKit.FuncC();
            ToolKit.FuncD();
            Console.ReadKey();
        }
    }
}

#define Buged //C#的宏定义必须在所有代码之前。当前只让Buged宏有效
//#define NoBug
//#define Li
//#define Zhang
using System;
using System.Diagnostics;
namespace AttributeTest
{
    public class ToolKit
    {
        [Conditional("Li")]
        [Conditional("Buged")]
        public static void FuncA()
        {
            Console.WriteLine("Created by Li, Buged");
        }

        [Conditional("Li")]
        [Conditional("NoBug")]
        public static void FuncB()
        {
            Console.WriteLine("Created by Li, NoBug");
        }

        [Conditional("Zhang")]
        [Conditional("Buged")]
        public static void FuncC()
        {
            Console.WriteLine("Created by Zhang, Buged");
        }

        [Conditional("Zhang")]
        [Conditional("NoBug")]
        public static void FuncD()
        {
            Console.WriteLine("Created by Zhang, NoBug");
        }
    }
}

运行结果如下:
这里写图片描述
注意:运行结果是由代码中“#define Buged ”这个宏定义所决定。

五、系统Attribute范例分析

1、在本例中,我们使用了ConditionalAttribute这个Attribute,它被包含在System.Diagnostics命名空间中,多半时间是用来做程序调试与诊断的。
2、与ConditionalAttribute相关的是一组C#宏,它们看起来与C语言的宏别无二致,位置必须在所有C#代码之前。顾名思义,ConditionalAttribute是用来判断条件的,凡被ConditionalAttribute(或Conditional)”附着”了的方法,只有满足了条件才会执行。
3、就像船底上可以附着很多牡蛎一样,一个方法上也可以附着多个ConditionalAttribute的实例。把Attribute附着在目标上的书写格式很简单,使用方括号把Attribute括起来,然后紧接着写Attribute的附着体即可。当多个Attribute附着在同一个目标上时,就把这些Attribute的方括号一个挨一个地书写(或者在一对方括号中书写多个Attribute),而且不必在乎它们的顺序。
4、在使用Attribute的时候,有“长记法”和“短记法”两种。

由上面的第三条和第四条我们可以推出,以下四种Attribute的使用方式完全等价:

//长记法
[ConditionalAttribute("Li")]
[ConditionalAttribute("NoBug")]
public static void Func()
{Console.WriteLine("Created by Li, NoBug"); }
//短记法
[Conditional("Li")]
[Conditional("NoBug")]
public static void Func()
{Console.WriteLine("Created by Li, NoBug"); }
//换序
[Conditional("NoBug")]
[Conditional("Li")]
public static void Func()
{Console.WriteLine("Created by Li, NoBug"); }
//单括号叠加
[Conditional("NoBug"),Conditional("Li")]
public static void Func()
{Console.WriteLine("Created by Li, NoBug"); }

参考文章:
深入浅出Attribute (上)——Attribute初体验
深入浅出Attribute (中)——Attribute本质论
我很想知道写这两篇文章的作者,他是在哪里获得这些知识的,或者说他在写这两篇文章时又参考了哪些资料呢?

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

changuncle

若恰好帮到您,请随心打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值