特性(C#)

特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签,可以为程序集、类型,以及类型内部的各种成员添加扩展信息,用于表示一些附加信息。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。通常,表示特性的类都派生自System.Attribute类。下面来看几个特殊的特性:

AttributeUsage

预定义特性 AttributeUsage 描述了如何使用一个自定义特性类。它规定了特性可应用到的项目的类型。

规定该特性的语法如下:

[AttributeUsage(
   validon,
   AllowMultiple=allowmultiple,
   Inherited=inherited
)]

其中:

  • 参数 validon 规定特性可被放置的语言元素。它是枚举器 AttributeTargets 的值的组合。默认值是 AttributeTargets.All
  • 参数 allowmultiple(可选的)为该特性的 AllowMultiple 属性(property)提供一个布尔值。如果为 true,则该特性是多用的。默认值是 false(单用的)。
  • 参数 inherited(可选的)为该特性的 Inherited 属性(property)提供一个布尔值。如果为 true,则该特性可被派生类继承。默认值是 false(不被继承)。

例如:

[AttributeUsage(AttributeTargets.Class|
            AttributeTargets.Struct|
            AttributeTargets.Enum|
            AttributeTargets.Delegate|
            AttributeTargets.Method|
            AttributeTargets.Property|
            AttributeTargets.Field|
            AttributeTargets.Constructor|
            AttributeTargets.Event,
            AllowMultiple=true,Inherited=false)]

再看下面代码:

        [Serializable]
        public class A { }

从上面代码,可以看出,SerializableAttribute特性将应用于A类,在C#中,可以略去“Attribute”,直接写“Serializable”即可。SerializableAttribute类的原型定义如下:

        [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct |
            AttributeTargets.Enum | AttributeTargets.Delegate, Inherited = false)]
        [ComVisible(true)]
        public sealed class SerializableAttribute : Attribute { }

从查看SerializableAttribute类的定义我们又发现,特性类除了从Attribute派生外,也可以向其附加特性,而用的最多的就是AttributeUsageAttribute特性,它的定义如下:

        [Serializable]
        [AttributeUsage(AttributeTargets.Class, Inherited = true)]
        [ComVisible(true)]
        public sealed class AttributeUsageAttribute : Attribute { }

在定义AttributeUsageAttribute类时也附加了自身作为类的特性,该类指定特性类的适用范围,用AttributeTargets枚举来表示。

如果特性存在带参数的构造函数,可以在特性后用小括号包裹起来,然后传递参数。当然也可以为特性类的属性或字段赋值,也可以同时加多个特性。

自定义特性

定义特性类与定义普通类一样的,既可以声明构造函数、字段、属性、方法等成员,也可以派生子类,但必须从System.Attribute类或System.Attribute的子类派生。

例如:

[AttributeUsage(AttributeTargets.Class
            | AttributeTargets.Method
            | AttributeTargets.Property)]
        public class MyAttribute : Attribute
        {
            public string Title { get; set; }
            public string VerNo { get; set; }
        }
        //特性用于类
       [My(Title="draw" ,VerNo="1.0.2")]
        public class Drawer
        {
           //特性用于属性
           [My(Title = "color", VerNo = "1..0.2")]
           public string Color { get; set; }
           //特性用于方法
           [My(Title = "color", VerNo = "1.0.2")]
           public void Draw() { }
           //特性用于字段,编译错误
           [My(Title = "thick", VerNo = "1.0.2")]
           public int a;
        }

注意,当MyAttribute特性用于字段时,会发生编译错误,因为MyAttribute类在定义是已经指明它只能用于类、方法、属性,但并未指明其可用于字段。

在默认条件下,特性将应用于更随其后的对象,同理,也可以为方法中的参数应用特性。如下代码所示:

        public static string Run([In]string pt,[Optional]int x)
        {
            return string.Empty;
        }

为参数应用特性秩序放在参数前面即可。但是,如果要为返回值应用特性,那么是不是把特性放在返回值前面就可以了呢?就像这样:

public [MarshalAs(UnmanagedType.SysInt] int Compute(){}

这样做是错误的,编译无法通过,那又如何实现呢?

在前面也提到,在默认的情况下,特性是应用于跟随其后的对象的,因此,在许多时候,使用特性是都会省略了表示特性目标的关键字。以下是特性应用于目标对象时的完整格式:

[<目标>:<特性列表>]

下面列举特性目标关键字及相关说明:

assembly:表示特性将应用于当前程序集,通常放在程序集中命名空间或所有类型定义之前。

module:用于当前模块。

field:用于字段,如果特性后紧跟着字段的声明代码,则该关键字可以省略。

event:用于事件。

method:用于方法,也可以用于属性中的get和set访问器。

param:用于方法中的参数或属性定义中的set访问器中的参数(value)。

property:用于属性。

type:用于类型。

return:用于方法的返回值,属性中的get访问器的返回值。

通过上面的介绍,相信你已经知道如何为方法的返回值应用特性了。即将特性应用到方法上,并且注明特性的应用目标为return。如下面代码所示:

        [return: MarshalAs(UnmanagedType.SysInt)]
        public int Compute() { return 0; }

通过反射技术检索特性

下面将介绍如何查找特性,需要用到反射技术。前面讲过,特性可以理解为附加在类型上的扩展信息,可以通过在类型中找到指定的特性来验证代码的调用方法是否符合特定的要求。

下面看个例子即可:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6
 7 namespace My
 8 {
 9     [AttributeUsage(AttributeTargets.All)]
10     public class TypeInfoAttribute : Attribute
11     {
12         public string Description { get; set; }
13     }
14
15     [TypeInfo(Description = "这是我们定义的枚举类型。")]
16     enum TestEnum { One = 1, Two, Three }
17
18     [TypeInfo(Description = "这是我们定义的一个类。")]
19     public class Goods { }
20
21     class Program
22     {
23         static void Main(string[] args)
24         {
25             // 用Type类的GetCustomAttributes方法可以获取指定类型上附加的特性列表
26             // 返回一个object类型的数组,数组中的每个元素表示一个特性类的实例
27             // GetCustomAttributes方法的其中一个重载可以将一个Type作为参数传递
28             // 该Type表示要获取的特性的类型,typeof运算符返回某个类型的一个Type
29             // 本例中我们要获取TypeInfoAttribute特性列表
30             // 由于上面定义TestEnum枚举和Goods类时,只应用了一个TypeInfoAttribute特性
31             // 因此获取到的特性实例数组的元素个数总为1
32
33             object[] attrs = typeof(TestEnum).GetCustomAttributes(typeof(TypeInfoAttribute), false);
34             if (attrs.Length > 0)
35             {
36                 TypeInfoAttribute ti = (TypeInfoAttribute)attrs[0];
37                 Console.WriteLine("TestEnum枚举的描述信息:{0}", ti.Description);
38             }
39
40             attrs = typeof(Goods).GetCustomAttributes(typeof(TypeInfoAttribute), false);
41             if (attrs.Length > 0)
42             {
43                 TypeInfoAttribute ti = (TypeInfoAttribute)attrs[0];
44                 Console.WriteLine("Goods类的描述信息:{0}", ti.Description);
45             }
46
47             Console.Read();
48         }
49     }
50 }

结果:

TestEnum枚举的描述信息:这是我们定义的枚举类型。
Goods类的描述信息:这是我们定义的一个类。

通过以上的介绍,相信你已经对特性有了一定的了解。博客写到这里,我也不啰嗦了。
时间: 2025-01-02 20:24:24

特性(C#)的相关文章

web新特性 之 WebSocket

详情参见:你真的了解WebSocket吗?     WebSocket系列教程   HTML5新特性之WebSocket WebSocket协议是基于TCP的一种新的协议.WebSocket最初在HTML5规范中被引用为TCP连接,作为基于TCP的套接字API的占位符.它实现了浏览器与服务器全双工(full-duplex)通信.其本质是保持TCP连接,在浏览器和服务端通过Socket进行通信. 服务端与客户端的连接不断开,实现全双工的操作.及服务端或是客户端都会给对方发送消息. WebSocke

ASP.NET MVC 使用Remote特性实现远程属性验证

RemoteAttribute是asp.net mvc 的一个验证特性,它位于System.Web.Mvc命名空间 下面通过例子来说明 很多系统中都有会员这个功能,会员在前台注册时,用户名不能与现有的用户名重复,还要求输入手机号码去注册,同时手机号码也需要验证是否重复,下面是实体类 /// <summary> /// 会员 /// </summary> public class Member { public int Id { get; set; } [Required(Error

C#图解教程 第二十四章 反射和特性

反射和特性元数据和反射Type 类获取Type对象什么是特性应用特性预定义的保留的特性Obsolete(废弃)特性Conditional特性调用者信息特性DebuggerStepThrough 特性其他预定义特性有关应用特性的更多内容多个特性其他类型的目标全局特性自定义特性声明自定义特性使用特性的构造函数指定构造函数使用构造函数构造函数中的位置参数和命名参数限制特性的使用自定义特性的最佳实践访问特性使用IsDefined方法使用GetCustomAttributes方法 Note 类的元数据包含

Java精品高级课,架构课,java8新特性,P2P金融项目,程序设计,功能设计,数据库设计,第三方支付,web安全,视频教程

36套精品Java架构师,高并发,高性能,高可用,分布式,集群,电商,缓存,性能调优,设计模式,项目实战,P2P金融项目,大型分布式电商实战视频教程 视频课程包含: 高级Java架构师包含:Spring boot.Spring  cloud.Dubbo.Elasticsearch,Redis.ActiveMQ.Nginx.Mycat.Spring.MongoDB.ZeroMQ.Git.Nosql.Jvm.Mecached.Netty.Nio.Mina.java8新特性,P2P金融项目,程序设计,

数据库事务的四大特性和事务隔离级别

Reference: [1] http://www.cnblogs.com/fjdingsd/p/5273008.html [2] http://blog.csdn.net/fg2006/article/details/6937413 数据库事务四大特性 如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性: ⑴ 原子性(Atomicity) 原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,这和前面两篇博客介绍事务的功能是一样的概念,因此事务的操作如果成功就必须要完全

java特性之继承

继承这一特性是面向对象的重要概念,好处就是提高代码的复用,节约开发时间. 在java中继承是指在父类的基础上扩展功能,继承中分为子类和父类. 类有两种重要成员:成员变量和方法. java中子类通过关键字extends可以获得父类的成员变量和方法.子类的成员中可以有自己声明定义的变量,也有从父类继承的. java中继承的特点: 1.单根继承,向上只有一个节点,所有的类继承的根节点都是Object类. 2.java不支持多继承.一个类不能同时继承多个类*(可以实现多喝接口). 3.子类重写父类的方法

js的ES6特性

一. let和const关键字 let出现之前,js所有的作用域都是以函数为单位的,只要在一个function里声明的var, 无论是for循环等块里面声明的还是在块外面声明的,整个function都可以使用这个var,比如: function foo() { for (var i=0; i<100; i++) { // } i += 100; // 仍然可以引用变量i } 我个人的理解是js的提升特性,函数会将里面声明的所有var都提升到函数开始的地方,所以整个函数内都共享这些var. let

C#网络程序设计(1)网络编程常识与C#常用特性

    网络程序设计能够帮我们了解联网应用的底层通信原理!     (1)网络编程常识: 1)什么是网络编程 只有主要实现进程(线程)相互通信和基本的网络应用原理性(协议)功能的程序,才能算是真正的网络编程. 2)网络编程的层次 现实中的互联网是按照"TCP/IP分层协议栈"的体系结构构建的,因此程序员必须搞清楚自己要做的是哪个层次上的编程工作. TCP/IP协议体系的实现情况: 其中,网络接口层已经被大多数计算机生产厂家集成在了主板上,也就是经常所说的网卡(NIC).windows操

Atitit js es5 es6新特性 attilax总结

1.1. JavaScript发展时间轴:1 1.2. 以下是ES6排名前十的最佳特性列表(排名不分先后):1 1.3. Es6 支持情况 基本chrome ff 360se8全面支持了2 2. ECMAScript 2015(ES6)的十大特征 – WEB前端开发 - 专注前端开发,关注用户体验.html2 1.1. JavaScript发展时间轴: 1.1995:JavaScript诞生,它的初始名叫LiveScript. 2.1997:ECMAScript标准确立. 3.1999:ES3出

NET(C#):XmlArrayItem特性和XmlElement特性在序列化数组的差别

https://www.mgenware.com/blog/?p=142 比如这样一个类,我们用XmlArrayItem特性标明数组内出现的元素类型: public class a{ [XmlArrayItem(Type = typeof(int)), XmlArrayItem(Type = typeof(Guid)), XmlArrayItem(Type = typeof(string))] public object[] arr = new object[] { 12, "hehe"