C#自定义特性(Attribute)详解

在前面介绍的代码中有使用特性,这些特性都是Microsoft定义好的,作为.NET Framework类库的一部分,许多特性都得到了C#编译器的支持。
.NET Frmework也允许定义自己的特性。自定义特性允许把自定义元数据与程序元素关联起来。这些元数据是在编译过程中创建的,并嵌入到程序集中。这些特性不会影响编译过程,因为编译器不能识别它们,但这些特性在应用于程序元素时,可以在编译好的程序集中用作元数据。这些元数据在文档说明中很有用。使自定义特性起很大作用的是反射(https://www.jb51.net/article/244259.htm)技术,代码可以读取这些元数据,使用它们在运行期间作决策。

编写自定义特性

[FieldName("Social")]
        public string SocialNumber
        {
            ...
        }

当C#编译器发现SocialNumber属性应用了一个FieldName特性时,首先会把字符串Attribute追加到FieldName这个名称后面,形成一个组合名称FieldNameAttribute,然后在其搜索路径的所有名称空间(即在using语句中提及的名称空间)中搜索FieldNameAttribute类。但如果该特性的名称以字符串Attribute结尾,编译器就不会把这个字符串加到组合名称中。
因此上面的代码等价于:

[FieldNameAttribute("Social")]
        public string SocialNumber
        {
            ...
        }

1.AttributeUsage特性

自定义的特性类需要直接或间接派生自System.Attribute。这个类还应包含控制用法的信息:

  • *特性可以应用到哪些类型的程序元素上(类,结构,属性和方法等)
  • *特性是否可以多次应用到同一个程序元素上
  • *特性在应用到类或接口上时,是否由派生类和接口继承
  • *这个特性有哪些必选和可选参数

如果编译器找不到对应的特性类,或者找到一个这样的特性类,但使用特性的方式与特性类中的信息不匹配,编译器就会产生一个编译错误。
定义FieldNameAttribute特性

[AttributeUsage(AttributeTargets.Property, AllowMultiple=false, Inherited=false)]
        public class FieldNameAttribute:Attribute
        {
            private string name;
            public FieldNameAttribute(string name)
            {
                this.name = name;
            }
        }

特性类FieldNameAttribute本身用了一个特性System.AttributeUsage来标记。这是Microsoft定义的一个特性,C#编译器为它提供了特殊的支持。AttributeUsage主要用于标识自定义特性可以应用到哪些类型的程序元素上。这些信息由它的第一个参数AttributeTargets给出,该参数是必选的,其类型是枚举类型AttributeTargets。上面的例子,指定FieldNameAttribute特性只能应用到属性上。
AttributeTargets枚举的成员如下:

上面列出了可以应用该特性的所有程序元素。在把特性应用到程序元素上时,应把特性放在元素前面的方括号中:

  [FieldName("Social")]
  public string SocialNumber
  {
    ...
  }

但在应用到Assembly和Module时,特性可以应用到整个程序集或模块中,而不是应用到代码中的一个元素上,在这种情况下,这个特性可以放在源代码的任何地方,但需要使用关键字Assembly和Module作为前缀:

  [assembly:FieldName("Social")]
  [module:FieldName("Social")]

在指定自定义特性的有效目标元素时,可以使用OR运算符(|)把这些值组合起来:

  [AttributeUsage(AttributeTargets.Property |AttributeTargets.Field,
    AllowMultiple=false, Inherited=false)]
  public class FieldNameAttribute:Attribute
  {
    private string name;
    public FieldNameAttribute(string name)
    {
      this.name = name;
    }
  }

也可以使用AttributeTargets.All指定自定义特性可以应用到所有类型的程序元素上。
AttributeUsage特性还包含另外两个参数:AllowMultiple和Inherited。它们用不同的语法来指定:参数名 = 参数值,而不是只给出这些参数的值。这些参数是可选的。

  • AllowMultiple参数表示一个特性是否可以多次应用到同一项上。
  • Inherited参数表示应用到类或接口上的特性是否自动应用到所以派生的类或接口上。如果特性应用到方法或属性上,它就自动应用到它们的重写版本上。

2.指定特性参数

  [FieldName("Social")]
  public string SocialNumber
  {
    ...
  }

编译器会检查传递给特性的参数(在本例中,是一个字符串),并产兆该特性类中带这些参数的构造函数。如果找到匹配的构造函数,编译器就会把指定的元数据传递给程序集。如果找不到,就会生成一个编译错误。反射()会从程序集中读取元数据,并实例化它们表示的特性类。因此,编译器需要确保存在这样的构造函数,才能在运行期间实例化指定的特性。

3.特性的可选参数

在AttributeUsage特性中,使用参数名 = 参数值语法把可选参数添加到特性中。这种语法指定可选参数的名称和值,它通过特性类中的公共属性或字段起作用:

[AttributeUsage(AttributeTargets.Property, AllowMultiple=false, Inherited=false)]
        public class FieldNameAttribute:Attribute
        {
            private string name;
            public FieldNameAttribute(string name)
            {
                this.name = name;
            }

            public string Comment
            {
                get;set;
            }
        }

        [FieldName("Social",Comment="可选参数值")]
        public string SocialNumber
        {
            ...
        }

到此这篇关于C#自定义特性(Attribute)的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • C#中枚举的特性 FlagAttribute详解

    写在前面 枚举Enum 全称(Enumeration),即一种由一组称为枚举数列表的命名常量组成的独特类型.可以看出枚举的出现时为了使我们可以在程序中方便的使用一些特定值的常量,一般的使用大家都比较熟悉,本文主要介绍枚举的特性 FlagAttribute. FlagAttribute是什么? Flag 特性微软的解释是:指示可以将枚举作为位域(即一组标志)处理,FlagsAttribute属性就是枚举类型的一项可选属性,它的主要作用是可以将枚举作为位域处理(P.S. C#不支持位域).所谓位域是

  • C#如何创建自定义特性

    概述 特性(Attribute)是用于在运行时传递程序中各种元素(比如类.方法.结构.枚举.组件等)的行为信息的声明性标签.您可以通过使用特性向程序添加声明性信息.一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的.要设计你自己的自定义特性,无需掌握许多新的概念. 如果你熟悉面向对象的编程,并且知道如何设计类,那么你已经具备大部分所需知识. 自定义特性本质上是直接或间接派生自 System.Attribute的传统类. 与传统类一样,自定义特性包含用于存储和检索数据的方法.

  • 浅析C# 9.0 新特性之 Lambda 弃元参数

    大家好,这是 C# 9.0 新特性短系列的第 5 篇文章. 弃元(Discards) 是在 C# 7.0 的时候开始支持的,它是一种人为丢弃不使用的临时虚拟变量.语法上它是用来赋值的,但它却不被分配存储空间,即没有值,所以不能从中读取值.弃元用 _(下划线) 表示,下划线是一个关键字,只能赋值,不能读取,例如: 在 C# 7.0 中,弃元的使用场景主要有下面四种: 元组和对象的解构 使用 is 和 switch 的模式匹配 对具有 out 参数的方法的调用 作用域内独立使用场景 针对这几个场景,

  • c# 反射+自定义特性保存数据至本地

    最近在项目过程中遇到了保存数据的需求,对实体类的部分数据进行保存,打算采用反射+自定义特性来实现数据保存,利于扩展 1. 采用反射实现能够灵活获取要保存的数据,由于只需要保存实体类(Model)的部分数据,因此采用自定义特性标记需要保存的数据,同时数据要求以.csv格式保存,添加自定义特性有利于对表头进行描述 2. 实现自定义特性 public class ResultAttribute : Attribute { private bool _IsSave; /// <summary> ///

  • 关于C# 4.0新特性“缺省参数”的实现详解

    前言 C#4.0关于缺省参数的新特性,相信大家都不会陌生.所谓缺省参数,顾名思义,就是在声明方法的某个参数的时候为之指定一个默认值,在调用该方法的时候如果采用该默认值,你就无须指定该参数.和很多语言层面特性(语法糖)的实现一样,缺省参数也是编译器为我们玩的一个小花招.缺省参数最终体现为两个特殊的自定义特性OptionalAttribute和DefaultParameterValueAttribute . 目录 一.缺省参数的用法 二.实现缺省参数的两个特性:OptionalAttribute和D

  • 浅谈C#9.0新特性之参数非空检查简化

    参数非空检查是缩写类库很常见的操作,在一个方法中要求参数不能为空,否则抛出相应的异常.比如: public static string HashPassword(string password) { if(password is null) { throw new ArgumentNullException(nameof(password)); } ... } 当异常发生时,调用者很容易知道是什么问题.如果不加这个检查,可能就会由系统抛出未将对象引用为实例之类的错误,这不利于调用者诊断错误. 由

  • C#开发教程之利用特性自定义数据导出到Excel

    网上C#导出Excel的方法有很多.但用来用去感觉不够自动化.于是花了点时间,利用特性做了个比较通用的导出方法.只需要根据实体类,自动导出想要的数据 1.在NuGet上安装Aspose.Cells或者用微软自带类库也可以 2.需要导出的数据的实例类: using System.ComponentModel; using System.Reflection; using System.Runtime.Serialization; public class OrderReport { [Displa

  • C#自定义特性(Attribute)详解

    在前面介绍的代码中有使用特性,这些特性都是Microsoft定义好的,作为.NET Framework类库的一部分,许多特性都得到了C#编译器的支持..NET Frmework也允许定义自己的特性.自定义特性允许把自定义元数据与程序元素关联起来.这些元数据是在编译过程中创建的,并嵌入到程序集中.这些特性不会影响编译过程,因为编译器不能识别它们,但这些特性在应用于程序元素时,可以在编译好的程序集中用作元数据.这些元数据在文档说明中很有用.使自定义特性起很大作用的是反射(https://www.jb

  • 微前端之Web组件自定义元素示例详解

    目录 我们知道的 Web组件使用 名称规范 组件传参数并可以写模板包括js和css Shadow Dom 影子节点 类中的构造函数和钩子函数 getter/setter属性和属性反射 扩展原生 HTML 我们知道的 第一:我们熟知的HTML标签有 a, p, div, section, ul, li, h2, article, head, body, strong, video, audio 等等 第二:我们知道,a标签是链接,p标签是段落,div是块级,h2是字体,strong 是粗体,vid

  • Spring Cloud Stream 高级特性使用详解

    目录 重试 消息发送失败的处理 消费错误处理 自定义MessageHandler类型 Endpoint端点 Metrics指标 Serverless Partition统一 Polling Consumer 支持多个Binder同时使用 建立事件机制 重试 Consumer端可以配置重试次数,当消息消费失败的时候会进行重试. 底层使用Spring Retry去重试,重试次数可自定义配置. # 默认重试次数为3,配置大于1时才会生效 spring.cloud.stream.bindings.<ch

  • Angularjs自定义指令Directive详解

    今天学习angularjs自定义指令Directive. Directive是一个非常棒的功能.可以实现我们自义的的功能方法. 下面的例子是演示用户在文本框输入的帐号是否为管理员的帐号"Admin". 在网页上放一个文本框和一个铵钮: <form id="form1" name="form1" ng-app="app" ng-controller="ctrl" novalidate> <i

  • Android使用xml自定义图片实例详解

    Android使用xml自定义图片实例详解 实现效果图: 白色圆角图片 bg_round_rectangle_white.xml <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <!-

  • Android启动内置APK和动态发送接收自定义广播实例详解

    Android启动内置APK和动态发送接收自定义广播实例详解 工作中遇到这样一个需求,需要为按键添加一个亲情号,提供一个接口启动内置的APK,思考再三决定更改Framework,利用广播机制去实现. 一.代码动态自主启动内置APK 我们都知道Android系统为我们提供了很多服务管理类,PackageManager主要是管理应用程序包,通过它就可以获取应用程序信息并构建Intent,启动对应的应用.除此之外Android还未我们提供了一些对应的类来管理相关的xml文件,比如说可以通过Packag

  • NodeJS自定义模块写法(详解)

    如下所示: //1.创建测试模块js文件(我这里命名为test.js) //2.添加测试方法 function test(){ console.log('Test Success!'); } //3.公开该方法到node模块 //exports.test(这个是public的方法名,外部调用的时候,使用这个方法名) exports.test = test; //4.测试(在另一个js文件中引入这个模块,并调用对应测试函数,两个js文件在同一目录下) const testModule = requ

  • 微信小程序 自定义对话框实例详解

    微信小程序 自定义对话框实例详解 效果图: index.wxml: <button type="default" bindtap="clickbtn"> 点击 </button> <view class="commodity_screen" bindtap="hideModal" wx:if="{{showModalStatus}}"></view> <

  • layui lay-verify form表单自定义验证规则详解

    官方文档详见:https://www.layui.com/doc/modules/form.html#verify 虽然layui的官方文档已经是写的比较详细,但是初次使用的时候总会懵一下,这里纪录一下lay-verify自定义验证规则的时候到底放哪. html: <input type="text" lay-verify="digital" placeholder="请输入数字"> <button type="but

  • 基于Laravel-admin 后台的自定义页面用法详解

    Laravel-admin 这个后台很好用,几乎省去了html和js的困扰,让后台CURD变得优雅简洁. 这是一个自定义面的Demo 路由定义: $router->get('mails/send', 'MailController@send'); $router->post('mails/send', 'MailController@send'); 控制中写法: public function send(Content $content) { //添加请求 if (request()->

随机推荐