当前位置: 首页 > news >正文

C# 反射和特性(自定义特性)

自定义特性

你或许已经注意到了,应用特性的语法和之前见过的其他语法有很大不同。你可能会觉得特
性是一种完全不同的结构类型,其实不是,特性只是一种特殊的类。
有关特性类的一些要点如下。

  • 用户自定义的特性类叫作自定义特性。
  • 所有特性类都派生自System.Attribute。

声明自定义特性

总体来说,声明一个特性类和声明其他类一样。然而,有一些事项值得注意,如下所示。

  • 要声明一个自定义特性,需要做如下工作。
    • 声明一个派生自System.Attribute的类。
    • 给它起一个以后缀Attribute结尾的名字。
  • 安全起见,通常建议你声明一个sealed的特性类。
    例如,下面的代码显示了MyAttributeAttribute特性的声明的开始部分:

image
由于特性持有目标的信息,所有特性类的公有成员只能是:

  • 字段
  • 属性
  • 构造函数

使用特性的构造函数

特性和其他类一样,有构造函数。每一个特性必须至少有一个公共构造函数。

  • 和其他类一样,如果你不声明构造函数,编译器会为你产生一个隐式、公共且无参的构
    造函数。
  • 特性的构造函数和其他构造函数一样,可以被重载。
  • 声明构造函数时必须使用类全名,包括后缀。只可以在应用特性时使用短名称。
    例如,如果有如下的构造函数(名字没有包含后缀),编译器会产生一个错误消息
public MyAttributeAttribute(string desc,string ver)
{Description = desc;VersionNumber = ver;
}

指定构造函数

当我们为目标应用特性时,其实是在指定应该使用哪个构造函数来创建特性的实例。列在特
性应用中的参数其实就是构造函数的实参。
例如,在下面的代码中,MyAttribute被应用到一个字段和一个方法上。对于字段,声明指定
了使用带单个字符串参数的构造函数。对于方法,声明指定了使用带两个字符串参数的构造函数。

[MyAttribute("Holds a value")]  //使用一个字符串的构造函数
public int MyField;[MyAttribute("Version 1.3", "Galen Daniel")] //使用两个字符串的构造函数
public void MyMethod()
{...
}

特性构造函数的其他要点如下。

  • 在应用特性时,构造函数的实参必须是在编译时能确定值的常量表达式。
  • 如果应用的特性构造函数没有参数,可以省略圆括号。例如,如下代码中的两个类都使
    用MyAttr特性的无参构造函数。两种形式的意义是相同的。
[MyAttr]
class SomeClass...[MyAttr]
class OtherClass...

使用构造函数

和其他类一样,我们不能显式调用构造函数。特性的实例被创建后,只有特性的消费者访问
特性时才能调用构造函数。这一点与其他类的实例不同,这些实例都创建在使用对象创建表达式
的位置。应用一个特性是一条声明语句,它不会决定什么时候构造特性类的对象。
图25-4比较了普通类构造函数的使用和特性的构造函数的使用。

  • 命令语句的实际意义是:“在这里创建新的类。
  • 声明语句的意义是:“这个特性和这个目标相关联,如果需要构造特性,则使用这个构造
    函数。

构造函数中的位置参数和命名参数

与普通类的方法和构造函数相似,特性的构造函数同样可以使用位置参数和命名参数。
如下代码显示了使用一个位置参数和两个命名参数来应用一个特性:

[MyAttribute("An execellent class",Reviewer="Amy McArthur",Ver="0.7.15.33)]

下面的代码演示了特性类的声明以及为MyClass类应用特性。注意,构造函数的声明只列出了
一个形参,但我们可通过命名参数给构造函数3个实参。两个命名参数设置了字段ver和Reviewer
的值。

public sealed class MyAttributeAttribute:System.Attribute
{public string Description;public string Ver;public string Reviewer;public MyAttributeAttribute(string desc) //一个形参{Description=desc;}
}[MyAttribute("An execellent class",Reviewer="Amy McArthur",Ver="7.15.33")]
class MyClass
{...
}

说明 和方法一样,构造函数需要的任何位置参数都必须放在命名参数之前。

限制特性的使用

我们已经看到了可以为类应用特性。但特性本身就是类,有一个很重要的预定义特性可以应
用到自定义特性上,那就是AttributeUsage特性。我们可以使用它来限制将特性用在某个目标类
例如,如果希望自定义特性MyAttribute只能应用到方法上,那么可以以如下形式使用
AttributeUsage:

[AttributeUsage(AttributeUsage.Method)]
public sealed class MyAttributeAttribute:System.Attribute
{...
}

AttributeUsage有3个重要的公有属性,如表25-4所示。表中显示了属性名和属性的含义。
对于后两个属性,还显示了它们的默认值。

名 字定 义默 认 值
ValidOn保存能应用特性的目标类型列表。构造函数的第一个参数必须是 AttributeTargets 类型的枚举值
Inherited一个布尔值,它指示特性是否可被装饰类型的派生类所继承true
AllowMultiple一个布尔值,指示目标上是否可应用特性的多个实例false

AttributeUsage的构造函数
AttributeUsage的构造函数接受单个位置参数,该参数指定了可使用特性的目标类型。它用这
个参数来设置Valid0n属性,可接受的目标类型是AttributeTargets枚举的成员。AttributeTargets
枚举的完整成员列表如表25-5所示。

可以通过使用按位或运算符来组合使用类型。例如,在下面的代码中,被装饰的特性只能应
用到方法和构造函数上。

[AttributeUsage(AttributeTarget.Method|AttributeTarget.Constructor)]
public sealed class MyAttributeAttribute:System.Attribute
{...
}
AllAssemblyClassConstructor
DelegateEnumEventField
GenericParameterInterfaceMethodModule
ParameterPropertyReturnValueStruct

当为特性声明应用AttributeUsage时,构造函数至少需要一个必需的参数,参数包含的目标
类型会保存在Valid0n中。还可以通过使用命名参数有选择地设置lnherited和AllowMu1tiple
属性。如果不设置,它们会保持如表25-4所示的默认值。
作为示例,下面一段代码指定了MyAttribute的如下方面。

  • MyAttlibute能且只能应用到类上。
  • MyAttribute不会被应用它的类的派生类所继承。
  • 不能在同一个目标上应用MyAttribute的多个实例。
[AttributeUsage(AttributeTarget.Class,//必需的位置参数Inherited=false,      //可选的命名参数AllowMultiple=false)] //可选的命名参数public sealed class MyAttributeAttribute:System.Attribute
{...
}

自定义特性的最佳实践

强烈推荐编写自定义特性时参考如下实践。

  • 特性类应该表示目标结构的某种状态。
  • 如果特性需要某些字段,可以通过包含具有位置参数的构造函数来收集数据,可选字段
    可以采用命名参数按需初始化。
  • 除了属性之外,不要实现公有方法或其他函数成员。
  • 为了更安全,把特性类声明为sealed。
  • 在特性声明中使用AttributeUsage来显式指定特性目标组。
    如下代码演示了这些准则:
[AttributeUsage(AttributeTargets.Class)]
public sealed class ReviewCommentAttribute:System.AttributeTargets
{public string Description{get;set;}public string VersionNumber{get;set;}public string ReviewerID{get;set;}public ReviewCommentAttribute(string desc,string ver){Description=desc;VersionNumber=ver;}
}
http://www.dtcms.com/a/336512.html

相关文章:

  • 股票术语:“支撑位”
  • 解码词嵌入向量的正负奥秘
  • 一张图总结 - AI代理上下文工程:构建Manus的经验教训
  • Python多线程、锁、多进程、异步编程
  • Linux | i.MX6ULL网络通信-套字节 TCP(第十七章)
  • 【k8s】Kubernetes核心概念与架构详解
  • 4.8 Vue 3: provide / inject 详解
  • LEA(Load Effective Address)指令
  • MACS2简介
  • 欠拟合和过拟合的特征标志,有什么方法解决,又该如何避免
  • 评测系统构建
  • 20.LeNet
  • [逆向知识] AST抽象语法树:混淆与反混淆的逻辑互换(二)
  • 2001-2024年中国玉米种植分布数据集
  • Cesium学习(二)-地形可视化处理
  • AutoSar BSW介绍
  • PyTorch 面试题及详细答案120题(01-05)-- 基础概念与安装
  • 全星质量管理 QMS:驱动制造业高质量发展的核心工具
  • 雷卯针对香橙派Orange Pi 5 Ultra开发板防雷防静电方案
  • Java研学-SpringCloud(五)
  • 如何理解“速度模式间接实现收放卷恒张力控制“
  • 题目2:使用递归CTE分析产品层级关系
  • 【从零开始学习Redis】项目实战-黑马点评D2
  • 【会议跟踪】ICRA 2021 Workshop:Visual-Inertial Navigation Systems
  • 多线程—飞机大战(加入播放音乐功能版本)
  • 【Virtual Globe 渲染技术笔记】6 着色
  • C语言---第一个C语言程序
  • Tomcat下载、安装及配置详细教程
  • Hybrid Beamforming Design for OFDM Dual-Function Radar-Communication System
  • LaTeX中表示实数集R的方法