【C#】Attribute

article/2025/10/7 1:53:41

原文链接:http://bbs.51aspx.com/showtopic-33963.html

前言

  作为一个.NET开发人员,了解Attribute的重要性,用.NET大师Jeffrey Richter的话就是“任何.NET Framework 开发人员都有必要对定制attribute有一个牢靠的掌握”,所以掌握Attitude,这是必须的!
  什么是Attribute(特性)?和Property(属性)是什么区别?
  我们来看看MSDN中对特性的描述:

Attribute 类将预定义的系统信息或用户定义的自定义信息与目标元素相关联。 目标元素可以是程序集、类、构造函数、委托、枚举、事件、字段、接口、方法、可移植可执行文件模块、参数、属性、返回值、结构或其他特性。特性在您编译代码时被发送到元数据中,并可通过运行时反射服务用于公共语言运行时以及任何自定义工具或应用程序。通俗地理解可以这么表述:你可以通过Attribute将一些额外信息加在一些目标元素上(类,字段,接口等),程序编译的时候就将这些额外的信息发送到元数据中,当你运行程序的时候可以通过反射技术从程序集元数据中读取这些额外信息,并根据这些额外信息决定你程序的行为。

Attribute和Property有什么区别?

  Attribute和Property有什么区别?其实这个问题是针对中文背景的开发者而言的,因为很多中文译本把Attribute和Property都翻译成属性,在这里为了区分,我们把Attribute翻译为特性,Attribute和Property基本没有什么瓜葛,因为它们是.NET中不同层面的东西,Property就是我们再熟悉不过的定义在类中的属性,它属于面向对象理论范畴,而Attribute是编程语言文法层面的东西,其定义在上面一段已经描述。

你使用过.NET定义好的Attribute吗?

  在.NET的基础类库中提供了很多定制好的Attribute供开发人员使用,这些定制的Attribute目的是方便开发者在代码中表达他们的意图。如下面三个Attribute类都是C#编译器能够理解的特性类:

名称说明
Obsolete这个属性用来标记不再使用的程序实体(如类或方法),每次使用标记为过时的实体时,会设设定此特性的方法,产生警告或错误。
Conditional该特性可以标示出某种环境设置下某个方法是否应该被调用。
Serializable指示一个类可以序列化。

下面以Obsolete特性的使用为例,说明Attribute是如何应用它的目标元素的。

namespace AttributeDemo
{class Program{static void Main(string[] args){MyClass myclass = new MyClass();myclass.OldMethod();Console.ReadKey();}}public class MyClass{[Obsolete("这是一个旧的方法,请调用新的方法NewMethod")]public void OldMethod(){MessageBox.Show("这是旧方法");}public void NewMethod(){MessageBox.Show("这是新方法");}}
}

调试这段程序的时候会发出警告信息,如下图所示:

这里写图片描述

  像Obsolete这样的定制特性,编译器能够做出相应处理,如在使用标记了Obsolete特性的方法时会发出警告信息,但如果我们使用自己定制的Attribute时,编译器会做什么处理呢?下面我们来自定义一个Attribute。

自定义Attribute

  为了符合“公共语言规范”(CLS),定制Attribute必须直接或间接从公共抽象类System.Attribute派生。所以我们前面提到Obsolete、Conditional和Serializable都是派生于Attribute。这里需要说明下的是自定制的Attribute的命名规范,其规则是“特性名+Attribute”,也就是我们自定制必须以Attribute为后缀,那么我们上面提到的三个特性都没有Attribute为后缀的呢,原来定义它们的时候都是有Attribute后缀的,如Obsolete是ObsoleteAttribute,只是我们将一个特性应用于某个目标元素时可以将Attribute这个后缀去掉,因为编译器会先查找没有Attribute后缀的特性,如果没有找到,则会查找加了Attribute后缀的特性名称。
  System.Attribute类的构造器被protected修饰,说明它不能自己实例化,只能被它的派生类调用。它有三个重要的静态方法,如下:

方法名称说明
GetCustomAttributes有多个重载,返回作用于目标的Attribute类实例的数组,也就是返回的类型是Attribute[]
GetCustomAttribute有多个重载,返回作用于目标的Attribute类的一个实例,如果目标没有应用任何的Attribute则返回null,如果目标应用了指定的Attribute的多个实例,就抛出一个System.Reflection.AmbiguousMatchException异常。
IsDefined如果至少有一个指定的Attribute派生类实例作用于目标,就返回true,否则返回false。这个方法效率很高,因为它不构建Attribute的实例,前面的两个方法返回都是Attribute实例,也就是需要从元数据中获取信息来构建实例,耗费性能多

  
  通常检查一个目标元素是否被应用了某个Attribute时,就调用System.Attribute.IsDefined方法,因为它的性能比GetCustomAttributes和GetCustomAttribute要高,如果需要返回Attribute的实例,则调用GetCustomAttributes或GetCustomAttribute方法。调用这三个方法都会扫描托管模块的元数据(因为Attribute是在编译的时候保存在托管模块的元数据上的),执行字符串比较来定义指定的Attribute类。这样的操作对时间性能消耗大,如果需要反复调用这些方法,可以缓存这些方法的调用结果,也就是把实例保存在全局变量中,不需要每次都扫描和构造实例。
  除了System.Attribute类提供的上面的三个静态方法可以检查目标元素应用Attribute的情况外,System.Reflection命名空间定义的一些类也允许你检查一个模块的元数据的内容,这些类包括Assembly,Module,ParameterInfo,MemberInfo,Type,MethodInfo,ConstrucorInfo,FieldInfo,EventInfo,PropertyInfo等,它们都提供了GetCustomAttributes和IsDefined方法。这些类GetCustomAttributes返回的类型是Object[],而System.Attribute类GetCustomAttributes方法返回的类型是Attribute[]。
  定义一个Attribute:

public class MyMsgAttribute:Attribute
{public string Msg { get; set; }public MyMsgAttribute(string msg){Msg = msg;}
}

  定义一个类,使自定制的MyMsgAttribute类能够应用在这个类上,如下:

[MyMsgAttribute("我的自定义Attribute")]
public class MyClass
{ 
}

  使用System.Reflection.Type提供的GetCustomAttributes方法获取MyMsgAttribute类的实例,代码如下:

var attributes = typeof(MyClass).GetCustomAttributes(typeof(MyMsgAttribute), true);
MyMsgAttribute myAttribute = attributes[0] as MyMsgAttribute;
if (myAttribute != null)
{Console.WriteLine(myAttribute.Msg);
}

使用System.Attribute提供的GetCustomAttributes方法获取MyMsgAttribute类的实例,代码如下:

var attributes2 = Attribute.GetCustomAttributes(typeof(MyClass));
MyMsgAttribute myAttribute2 = (MyMsgAttribute)attributes2[0];
Console.WriteLine(myAttribute2.Msg);

上面两段代码输出的结果都是Msg的属性值——“我的自定义Attribute”。
  经过上面两段代码的分析,我们已经知道自定义一个Attribute类,并使这个Attribute应用在一个类上,同时在了解了在运行时如何从元数据构造这个Attribute的实例之后,我们得到Attribute对象,就可以根据这个对象的信息来执行一些逻辑分支代码,上面只是简单地输出Attribute对象Msg属性值,可见,定制Attribute是非常有用的,因为它能在运行时决定我们执行不同的逻辑分支代码。如我们可以通过IsDefined检查一个类是否应用了SerializableAttribute,从而判断这个类是否可以用于系列化操作。

Attribute限定施加元素的类型

  在我们使用Attribute应用于目标元素的时候,我们会发现一个现象,就是有些Attribute可以应用于类,也可以应用于属性,如SerializableAttribute,而有些Attribute只能应用于方法这个目标元素,如ConditionalAttribute,为什么不同的Attribute会有这种应用目标元素的区别呢?我们查看这两个Attribute的定义,发现它们本身应用了一个Attribute类,这个Attribute类就是AttributeUsage。因为Attribute本身就是一个类,所以它是允许应用其它Attribute类的。而AttributeUsage的目的就是限定你的Attribute 所施加的元素的类型,比如限制你的Attribute能够应用于类还是方法或者属性上。
  AttributeUsage的构造函数有一个参数,这个参数是AttributeTargets的枚举类型。AttributeTargets的枚举成员名称说明如下:

方法名称说明
All可以对任何应用程序元素应用特性。
Assembly可以对程序集应用特性。
Class可以对类应用特性。
Constructor可以对构造函数应用特性。
Delegate可以对委托应用特性。
Enum可以对枚举应用特性。
Event可以对事件应用特性。
Field可以对字段应用特性。
GenericParameter可以对泛型参数应用特性。
Interface可以对接口应用特性。
Method可以对方法应用特性。
Module可以对模块应用特性。注意Module指的是可移植的可执行文件(.dll 或 .exe),而非Visual Basic标准模块。

  如果你的自定制的Attribute没有显示应用AttributeUsage,编译器会自动给你加上一个默认的AttributeUsage,而这个默认的构造函数参数就是AttributeTargets.All,也就是你的这个自定制Attribute能够应用下面元素类型为:Assembly | Module | Class | Struct | Enum | Constructor | Method | Property | Field | Event | Interface | Parameter | Delegate,
ClassMembers = Class | Struct | Enum | Constructor | Method | Property | Field | Event | Delegate | Interface 。
  如果你的自定制Attribute只想作用于类和方法,实例代码如下:

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method)]
public class MyMsgAttribute:Attribute
{public string Msg { get; set; }public MyMsgAttribute(string msg){Msg = msg;}
}

这样定义的MyMsgAttribute只能应用于类和方法,应用于其它类型的目标元素时编译的时候会报错。

AttributeUsage类的两个属性

  AttributeUsage类提供了两个公共的属性,AllowMultiple和Inherited。AllowMultiple是用来设置是否允许让多个Attribute实例应用在同一个目标元素上,当我们将AttributeUsage应用于自定制Attribute时,可以指定AllowMultiple属性值为True,这样自定制的Attribute就允许将它的多个实例应用于单个目标元素,如果不将AllowMultiple显示设为True,自定制的Attribute只能向一个选定的目标元素应用一次。Inherited属性指定自定制Attribute应用于基类时,是否同时应用于派生类和重写的方法。我们用代码演示AllowMultiple和Inherited的概念。

[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method,AllowMultiple=true,Inherited=true)]
public class MyMsgAttribute:Attribute
{public string Msg { get; set; }public MyMsgAttribute(string msg){Msg = msg;}
}[MyMsgAttribute("我的自定义Attribute")]
[MyMsgAttribute("也是你的自定义Attribute")]
public class MyClass
{public string Name { get; set; }public string GetName(){return Name;}
}public class YourClass : MyClass
{ }static void Main(string[] args)
{var attributes = typeof(MyClass).GetCustomAttributes(typeof(MyMsgAttribute), true);foreach (var attribute in attributes){MyMsgAttribute myAttribute = attribute as MyMsgAttribute;if (myAttribute != null){Console.WriteLine(myAttribute.Msg);}}attributes = typeof(YourClass).GetCustomAttributes(typeof(MyMsgAttribute), true);foreach (var attribute in attributes){MyMsgAttribute myAttribute = attribute as MyMsgAttribute;if (myAttribute != null){Console.WriteLine(myAttribute.Msg);}}Console.ReadKey();
}

输出的结果为:

这里写图片描述

总结

  我们根据上面文章的分析对自定制Attribute的定义和使用进行总结:
  
  1、自定制的Attribute必须派生于System.Attribute。
  2、在自定制的Attribute应用AttributeUsageAttribute可以对自定制的Attribute进行应用目标元素、目标元素是否支持应用同一个Attribute多个实例,目标元素应用的Attribute是否能应用于的派生类和派生类的重写方法等进行控制。
  3、自定制的Attribute是在编译时保存在模块的元数据上的,在运行时从元数据读取信息来构建Attribute实例。
  4、获取或判断某个目标元素应用Attribute的信息,可以通过System.Attribute提供的三个方法:System.Attribute.IsDefined,
System.Attribute.GetCustomAttributes和System.Attribute.GetCustomAttribute,也可以通过System.Reflection命名空间定义的一些类来检查一个模块的元数据的内容,这些类包括Assembly,Module,ParameterInfo,MemberInfo,Type,MethodInfo,ConstrucorInfo,FieldInfo,EventInfo,PropertyInfo等,它们都提供了GetCustomAttributes和IsDefined方法。


http://chatgpt.dhexx.cn/article/r3F1OEfw.shtml

相关文章

attributes() 函数

查看更多 https://www.yuque.com/docs/share/a6cc2c96-9824-4903-acb8-284f4ebeb4fb

__attribute__ 用法

转自:http://www.cnblogs.com/astwish/p/3460618.html GNU C 的一大特色就是__attribute__ 机制。__attribute__ 可以设置函数属性(Function Attribute )、变量属性(Variable Attribute )和类型属性(Type …

View绘制体系(三)——AttributeSet与TypedArray详解

View绘制体系(三)——AttributeSet与TypedArray详解 前言 上篇博客中讲了LayoutInflater.inflate机制,其中提到了AttributeSet和XmlPullParser两个接口,这里我们来详细的了解一下Android中提供的AttributeSet接口和它与XmlPullPa…

Android自定义View(三)自定义属性AttributeSet

自定义View的时候通常需要提供一些自定义属性,自定义属性非常简单,只需要在res资源目录的values目录下创建一个attrs.xml的属性定义文件,然后在该文件中定义相应的属性,并在自定义View的构造函数中获取并设置自定义属性的默认值即…

web搜索框的制作(必应)

前两天没事突然对搜索来了兴趣,我一直在想搜索框中我们输入一些字或者字母,为何下面就会有一些自动补齐的相关搜索,比如我在搜索输入框中输入一个字母e,下面就会出现饿了么,e租宝,ems等相关的搜索链接。然后…

html怎么调搜索框宽高,百度站内搜索css:输入框宽度及样式自定义

近日网站使用了百度站内搜索api,目的是为了提高站内搜索的速度,减轻查询站内数据库带来的服务器压力。 不过在使用百度站内搜索api(生效范围:*webkaka.com/*)后发现一个问题,不同的频道模版造成排版不合适的后果,如搜索…

织梦手机站站内搜索

今天在做手机网站发现一个问题,当在手机是使用搜索功能时马上就跳转到电脑端网站去了,在手机上无法使用。在网上找半天没有找到解决的办法,后来自己想通了,下面告诉大家怎么样简单的实现这个功能!我的手机站是在m/这个…

基于 Elasticsearch 的站内搜索引擎实战

站内搜索,可以认为是针对一个网站特性内容的搜索功能。由于内容、格式可控,站内搜索比全网搜索的实现要简单很多。 简书这个网站本身自带一个搜索,但是缺乏针对个人文章的搜索,所以本文的实战内容是解决这个痛点。 代码在 https…

使用swiftype实现站内搜索

本人博客opiece.me,欢迎访问。 前言 首先,以下的内容是基于最新的swifytpe的教程,应该是2.0.0。 站内搜索顾名思义就是将范围限定在你的网站内,以此范围进行关键字搜索。 常见的站内搜索是google和baidu的,但是现在…

Compass实战 站内搜索

今天早上打算对这两天学习的Lucene以及Compass总结一下,想来想去,还是写个小项目来验证最好了。于是就有了今天的这篇文章。难易程度适合对于Compass或者Lucene刚入门的童鞋,大牛看到后望轻喷 :-) 项目预览项目需求项目目录核心处理 发帖部分查询部分总结项目预览 项目需求 …

html中的搜索

目录 hello😄 form表单🍉 form的语法🍊 from的属性🍊 提交?重置?🍊 表单按钮(html)🔍 JavaScript提交表单🔍 JavaScript重置表单&#x1f…

必应(Bing)的站内搜索 site:<域名> <搜索内容>

最近在备考OCP,发现有一个网站的题库很好,就是www.examtopics.com,有很多Oracle的考题,都是在这里面搜到的,而且每道题都有人讨论。 为了加快搜索速度,提高精度,可以用Bing在这个网站内搜索&am…

百度站内搜索使用教程

最近做了一个博客CMS网站,用到了百度站内搜索,做一些必要的笔记,一来是对自己学习的知识的巩固,二来对有同样问题的人有参考作用 文章目录 一 使自己的网站被百度收录二 获取百度站内搜索代码三 总结 声明一下,我本人很…

利用免费的必应 Bing 自定义搜索打造站内全文搜索

简介 百度的站内搜索不做了,唉,果然免费的不永久。我们看看 Bing 的,每个月有 1000 次免费的调用 bing search api 的次数。不同客户可以多申请几个就行了。 申请入口: https://www.customsearch.ai,官方简介页面官方…

html百度站内搜索代码,网站添加百度站内搜索的教程

zblog博客程序中可以在侧边栏中添加搜索功能,但是让人郁闷的是如果没针对搜索使用搜索插件,那情况简直让人抓狂,还好我们可以使用百度的站内搜索功能,一方面可以节省网站的资源,另一方面可以增加百度的收率几率。 关于…

站内搜索

使用“site:”或者“domain:”来实现站内搜索 如果你想在一个特定的网站上来进行搜索,在众多庞大的信息流中找到你想要的信息, 在上篇中(http://blog.csdn.net/liunian02050328/article/details/8220379)介绍在java编程的环境下实现站内搜索,…

计算机网络中的ping什么意思,PING命令是什么?PING使用方法和参数详解

PING命令是用来检查本机于网络上的电脑是否正常通信的一个命令,作为一个网站的管理员、单位的网管这也是一个必会的命令。 因为网络中所有的电脑都有一个单独不会重复的IP地址,我们使用PING命令给目标IP地址发送一个数据包,对方就要返回一个同…

常见的ping命令

1.ping 延时和丢包 开始--运行---输入cmd---输入ping IP(IP为所要ping的服务器的IP) 常与 -t 选项结合使用 ctrlc结束 延时主要看时间列 看时间得数值和波动 丢包 ---出现请求超时 2.追踪路由 tracert IP 注意: 追路由 --一般追3次 …

ping命令常见参数使用详解

winR 输入cmd 回车 进入命令窗口 输入ping baidu.com 回车可以查看网络连接。 ping [-t]参数是用来不断的ping对方主机,直到手动停止,使用ctrlc。Windows默认是四次停止。 [-l](-L)参数用来设定数据包的大小的,在默认的…

Linux 常用ping命令详解

一、用法 Usage: ping [-aAbBdDfhLnOqrRUvV] [-c count] [-i interval] [-I interface][-m mark] [-M pmtudisc_option] [-l preload] [-p pattern] [-Q tos][-s packetsize] [-S sndbuf] [-t ttl] [-T timestamp_option][-w deadline] [-W timeout] [hop1 ...] destination二…