Attribute

      • 1.什么是特性
      • 2.应用特性
      • 3.预定义的保留的特性
        • 3.1 Obsolete特性
        • 3.2 Conditional特性
        • 3.3 调用者信息特性
        • 3.4 DebuggerStepThrough特性
      • 4.特性的其他内容
        • 4.1 多个特性
        • 4.2 其他类型的目标
        • 4.3 全局特性
      • 5.自定义特性
        • 5.1 声明自定义特性
        • 5.2 使用特性的构造函数
        • 5.3 指定构造函数
        • 5.4 构造函数中的位置参数和命名参数
        • 5.5 限制特性的使用
      • 6.访问特性
        • 6.1 使用IsDefined方法
        • 6.2 使用GetCustomAttributes方法

1.什么是特性

特性(attribute)是一种允许向程序的程序集增加元数据的语言结构,它是用于保存程序结构信息的某种特殊类型的类。

  • 将应用了特性的程序结构叫做目标
  • 设计用来获取和使用元数据的程序叫做特性的消费者
    image

2.应用特性

特性的目的是告诉编译器把程序结构的某组元数据嵌入程序集,可以通过把特性应用到结构来实现。

  • 在结构前放置特性片段来应用特性
  • 特性片段被方括号包围,其中是特性名和特性的参数列表
[ Serializable ] // 特性
public class MyClass {...} 
[ MyAttribute("Simple Class", "Version 0.0.1") ] // 带有参数的特性 public class MyOtherClass {...} 

3.预定义的保留的特性

3.1 Obsolete特性

可以使用Obsolete特性将程序结构标注为过期,并在代码编译时显示有用的警告信息。

class Program
{
    [Obsolete("Use new method!")]
    static void OldPrint() { Console.WriteLine("hello"); } static void Main() { OldPrint(); Console.ReadKey(); } } 

即使OldPrint被标注为过期,Main方法还是会调用它, output

hello

但是在编译过程中,编译器生成警告信息 waring

Warning	CS0618	'Program.OldPrint()' is obsolete: 'Use new method!'	ConsoleApp1	C:\Users\kylew\Desktop\ConsoleApp1\ConsoleApp1\Program.cs 12 Active 

另外一个Obsolete特性的重载接受了bool类型的第二个参数,用来指定目标是否应该被标记为错误而不仅仅是警告。

[Obsolete("Use new method!", true)] // 标记为错误
3.2 Conditional特性

Conditional特性允许包括或排斥特定方法的所有调用。

#define DoTrace
using System;
using System.Diagnostics;

class Program { [Conditional("DoTrace")] static void Print1() { Console.WriteLine("1111"); } static void Print2() { Console.WriteLine("2222"); } static void Main() { Print1(); Print2(); Console.ReadKey(); } } 

output

1111
2222

如果注释掉第一行来取消DoTrace的定义,
output

2222
3.3 调用者信息特性

调用者信息特性可以访问文件路径、代码行数、调用成员的名称等源代码信息。

  • 这三个特性为CallerFilePath、CallerLineNumber、CallerMenberName
  • 这些特性只能用于方法中的可选参数
using System;
using System.Runtime.CompilerServices;

class Program
{
    public static void MyTrace(string message, [CallerFilePath] string fileName = "", [CallerLineNumber] int lineNumber = 0, [CallerMemberName] string callingMenber = "") { Console.WriteLine($"File:{fileName}"); Console.WriteLine($"Line:{lineNumber}"); Console.WriteLine($"Called From:{callingMenber}"); Console.WriteLine($"Message:{message}"); } public static void Main() { MyTrace("simple message"); Console.ReadKey(); } } 

output

File:C:\Users\kylew\Desktop\ConsoleApp1\ConsoleApp1\Program.cs Line:19 Called From:Main Message:simple message 
3.4 DebuggerStepThrough特性

在单步调试时,希望调试器不进入某些方法,仅仅执行该方法,然后继续调试下一行。

using System;
using System.Diagnostics;

class Program
{
    [DebuggerStepThrough] public static int Method() // 单步调试时不进入方法体,直接执行该方法 { int sum = 0; for (int i = 0; i < 10; i++) sum += i; return sum; } public static void Main() { Console.WriteLine("start"); Console.WriteLine($"sum = {Method()}"); Console.ReadKey(); } } 

4.特性的其他内容

4.1 多个特性
[ Serializable ] 
[ MyAttribute("simple class", "version 0.0.1") ] // 多层结构 [ MyAttribute("simple class", "version 0.0.1"), Serializable ] // 逗号分隔 
4.2 其他类型的目标
[ MyAttribute("simple class", "version 0.0.1") ] // 字段上的特性
public int MyField; [ Obsolete ] // 方法上的特性 [ MyAttribute("simple class", "version 0.0.1") ] public void Print() {...} 

还可以显示的标注特性,从而将它用到特殊的目标结构,

[ method: MyAttribute("simple class", "version 0.0.1") ] 
[ return: MyAttribute("simple class", "version 0.0.1") ] public void Print() {...} 

特性目标:

eventfieldmethodparam
methodparampropertyreturn
typetypevarassemblymodule
4.3 全局特性

assembly和module用来将特性设置在程序集或模块级别:

  • 程序集级别的特性必须放置在任何命名空间之外,并且通常放置在AssemblyInfo.cs文件中
  • AssemblyInfo.cs文件通常包含有关公司、产品以及版权信息的元数据
[assembly: Assembly(MyTitle)]

5.自定义特性

5.1 声明自定义特性
  • 声明一个派生自System.Attribute的类
  • 特性名称以后缀Attribute结尾
  • 建议声明一个sealed的特性类
public sealed class MyAttributeAttribute: System.Attribute // 使用时可简写为MyAttribute {...} 

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

  • 字段
  • 属性
  • 构造函数
5.2 使用特性的构造函数

每一个特性至少必须有一个公共构造函数,若不声明构造函数,编译器会产生一个隐式、公共且无参的构造函数。

public MyAttributeAttribute(string desc, string ver) { Description = desc; VersionNumber = ver; } 
5.3 指定构造函数

为目标应用特性时,实际上是在指定应该使用哪个构造函数。

[MyAttribute("hello")] // 使用一个字符串的构造函数
public int MyField; [MyAttribute("hello", "world")] // 使用两个字符串的构造函数 public void MyMethod() {...} [MyAttribute] // 无参时可省略括号 
5.4 构造函数中的位置参数和命名参数
[MyAttribute("hello", Reviewer = "Kyle", Ver = "0.0.1")] 位置参数 命名参数 命名参数 
public sealed class MyAttributeAttribute:System.Attribute
{
    public string Description; public string Ver; public string Reviewer; public MyAttributeAttribute(string desc) { Description = desc; } } [MyAttribute("hello", Reviewer = "Kyle", Ver = "0.0.1")] class MyClass { ... } 
5.5 限制特性的使用

使用AttributeUsage特性来限制特性使用在某个目标类型上。

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

AttributeUsage有三个重要的公共属性:

名字意义默认值
ValidOn保存特性能应用到目标类型的列表。构造函数的第一个参数必须是AttributeTarget类型的枚举值 
Inherited一个布尔值,它指示特性是否会被装饰类型的派生类所继承true
AllowMultiple一个指示目标是否被应用多个特性的示例的布尔值false
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor)]
public sealed class MyAttributeAttribute:System.Attribute { ... } 

AttributeTarget枚举成员:

AllAssemblyClassConstructor
DelegateEnumEventField
GenericParameterInterfaceMethodModule
ParameterPropertyRetrunValueStruct

一个AttributeUsage的具体用法:

[AttributeUsage(AttributeTargets.Class, // 只能应用到类上
                Inherited = false, // 不会被应用它的派生类所继承 AllowMultiple = false)] // 不能有多个MyAttribute实例应用到同一目标上 public sealed class MyAttributeAttribute:System.Attribute { ... } 

6.访问特性

6.1 使用IsDefined方法

使用Type对象的IsDefined方法来检测某个特性是否应用到了某个类上。

[AttributeUsage(AttributeTargets.Class,
    Inherited = false,
    AllowMultiple = false)]
public sealed class MyAttributeAttribute:System.Attribute { public string Description; public string Ver; public string Reviewer; public MyAttributeAttribute(string desc) { Description = desc; } } [MyAttribute("hello", Reviewer = "Kyle", Ver = "0.0.1")] class MyClass { } class Program { static void Main() { MyClass mc = new MyClass(); Type t = mc.GetType(); bool isDefined = t.IsDefined(typeof(MyAttributeAttribute), false); if (isDefined) Console.WriteLine($"MyAttribute is applied to type {t.Name}"); Console.ReadKey(); } } 

output

MyAttribute is applied to type MyClass
6.2 使用GetCustomAttributes方法

GetCustomAttributes方法返回应用到结构的特性的数组。

  • 实际返回的对象时object的数组,因此要强制转换为相应的特性类型
  • 布尔参数指定是否搜索继承树来检查特性
    object[] AttArr = t.GetCustomAttributes(false);
  • 调用GetCustomAttributes方法后,每一个与目标相关联的特性的实例就会被创建
[AttributeUsage(AttributeTargets.Class)]
public sealed class MyAttributeAttribute:System.Attribute { public string Description; public string Ver; public string Reviewer; public MyAttributeAttribute(string desc,string ver) { Description = desc; Ver = ver; } } [MyAttribute("hello", "0.0.1")] class MyClass { } class Program { static void Main() { Type t = typeof(MyClass); object[] AttArr = t.GetCustomAttributes(false); foreach (Attribute a in AttArr) { MyAttributeAttribute attr = a as MyAttributeAttribute; if (null != attr) { Console.WriteLine($"Description: {attr.Description}"); Console.WriteLine($"Version: {attr.Ver}"); Console.WriteLine($"Reviewer: {attr.Reviewer}"); } } Console.ReadKey(); } } 

output

Description: hello
Version: 0.0.1
Reviewer:

转载于:https://www.cnblogs.com/jizhiqiliao/p/10649169.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值