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)是一种允许向程序的程序集增加元数据的语言结构,它是用于保存程序结构信息的某种特殊类型的类。
- 将应用了特性的程序结构叫做目标
- 设计用来获取和使用元数据的程序叫做特性的消费者
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() {...}
特性目标:
event | field | method | param |
---|---|---|---|
method | param | property | return |
type | typevar | assembly | module |
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枚举成员:
All | Assembly | Class | Constructor |
---|---|---|---|
Delegate | Enum | Event | Field |
GenericParameter | Interface | Method | Module |
Parameter | Property | RetrunValue | Struct |
一个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: