文章目录
特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。
特性(Attribute)用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息。.Net 框架提供了两种类型的特性:预定义特性和自定义特性。
规定特性(Attribute)
规定特性(Attribute)的语法如下:
[attribute(positional_parameters, name_parameter = value, ...)]
element
特性(Attribute)的名称和值是在方括号内规定的,放置在它所应用的元素之前。positional_parameters 规定必需的信息,name_parameter 规定可选的信息。
预定义特性(Attribute)
.Net 框架提供了三种预定义特性:
- AttributeUsage
- Conditional
- Obsolete
AttributeUsage
预定义特性 AttributeUsage 用于指定另一特性类的用法。它规定了特性可应用到的项目的类型。
规定该特性的语法如下:
[AttributeUsage(
validon,
AllowMultiple=allowmultiple,
Inherited=inherited
)]
其中:
- 参数 validon 规定特性可被放置的语言元素。它是枚举器 AttributeTargets 的值的组合。默认值是 AttributeTargets.All。
- 参数 allowmultiple(可选的)为该特性的 AllowMultiple 属性(property)提供一个布尔值。如果为 true,则该特性是多用的。默认值是 false(单用的)。
- 参数 inherited(可选的)为该特性的 Inherited 属性(property)提供一个布尔值。如果为 true,则该特性可被派生类继承。默认值是 false(不被继承)。
Conditional
这个预定义特性标记了一个条件方法,其执行依赖于它顶的预处理标识符。
它会引起方法调用的条件编译,取决于指定的值,比如 Debug 或 Trace。例如,当调试代码时显示变量的值。
规定该特性的语法如下:
[Conditional(
conditionalSymbol
)]
例如:
[Conditional("DEBUG")]
C# 中利用 Conditional 定义条件方法
利用 Conditional 属性,程序员可以定义条件方法。Conditional 属性通过测试条件编译符号来确定适用的条件。当运行到一个条件方法调用时,是否执行该调用,要根据出现该调用时是否已定义了此符号来确定。如果定义了此符号,则执行该调用;否则省略该调用(包括对调用的参数的计算)。
使用 Conditional 是封闭 #if 和 #endif 内部方法的替代方法,它更整洁,更别致、减少了出错的机会。
条件方法要受到以下限制:
- 条件方法必须是类声明或结构声明中的方法。如果在接口声明中的方法上指定 Conditional 属性,将出现编译时错误。
- 条件方法必须具有返回类型。
- 不能用 override 修饰符标记条件方法。但是,可以用 virtual 修饰符标记条件方法。此类方法的重写方法隐含为有条件的方法,而且不能用 Conditional 属性显式标记。
- 条件方法不能是接口方法的实现。否则将发生编译时错误。
- 如果条件方法用在“委托创建表达式”中,也会发生编译时错误
这里需要注意的是:如果创建一个没有定义任何条件的方法,那么默认只要调用就总是会执行此方法,如果你想通过条件来判断执行,那么该方法上必须至少包含一个conditional特性所定义的条件,它才会响应你定义的条件
Obsolete(基本不推荐,会影响编译器的运行)
这个预定义特性标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素。例如,当一个新方法被用在一个类中,但是您仍然想要保持类中的旧方法,您可以通过显示一个应该使用新方法,而不是旧方法的消息,来把它标记为 obsolete(过时的)。
[Obsolete(
message
)]
[Obsolete(
message,
iserror
)]
其中:
- 参数 message,是一个字符串,描述项目为什么过时的原因以及该替代使用什么。
- 参数 iserror,是一个布尔值。如果该值为 true,编译器应把该项目的使用当作一个错误。默认值是 false(编译器生成一个警告)。
创建自定义特性(Attribute)
.Net 框架允许创建自定义特性,用于存储声明性的信息,且可在运行时被检索。该信息根据设计标准和应用程序需要,可与任何目标元素相关。
创建并使用自定义特性包含四个步骤:
- 声明自定义特性
- 构建自定义特性
- 在目标程序元素上应用自定义特性
- 通过反射访问特性
最后一个步骤包含编写一个简单的程序来读取元数据以便查找各种符号。元数据是用于描述其他数据的数据和信息。该程序应使用反射来在运行时访问特性。我们将在下一章详细讨论这点。
声明构建自定义特性
一个新的自定义特性应派生自 System.Attribute 类。例如:
using System;
namespace MyAttribute
{
//声明自定义特性
[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)]
public class CustomAttribute : Attribute
{
public CustomAttribute(string dtime)
{
this.dtime = dtime;
}
public string Description { get; set; }
public string Remark = null;
private string dtime;
public string Dtime{ get { return dtime; } }
public void Show()
{
Console.WriteLine($"This is {nameof(CustomAttribute)}");
}
}
}
让我们构建一个名为 Custom 的自定义特性,该特性将标识的信息。它存储下面的信息:
- Description:描述
- Remark:标识信息
- Dtime:添加时间
该属性可应用到所有元素上,添加时间Dtime属性为必需的定位参数,Remark和Description为可选参数。
应用自定义特性
通过将把特性放置在紧接着它目标的上方,来应用改特性:
namespace MyAttribute
{
[Custom("2019-11-21", Description = "1234", Remark = "2345")]//方法不行
public class Student
{
[CustomAttribute]
public int Id { get; set; }
[CustomAttribute]
public void Study()
{
Console.WriteLine($"这里是{this.Id}");
}
[Custom()]
[return: Custom()]
public string Answer([Custom]string name)
{
return $"This is {name}";
}
}
}
此处通过 反射 方法检查 Student 类的各项成员是否应用了 CustomAttribut 特性,并将各项参数做打印处理。
using System;
using System.Reflection;
namespace MyAttribute
{
public class Manager
{
public static void Show(Student student)
{
Type type = typeof(Student); //student.GetType();
if (type.IsDefined(typeof(CustomAttribute), true))//检查类是否应用了CustomAttribute特性
{
CustomAttribute attribute = (CustomAttribute)type.GetCustomAttribute(typeof(CustomAttribute), true);
Console.WriteLine($"{attribute.DTime}_{attribute.Description}_{attribute.Remark}");
attribute.Show();
}
//属性
PropertyInfo property = type.GetProperty("Id");
if (property.IsDefined(typeof(CustomAttribute), true))
{
CustomAttribute attribute = (CustomAttribute)property.GetCustomAttribute(typeof(CustomAttribute), true);
Console.WriteLine($"{attribute.DTime}_{attribute.Description}_{attribute.Remark}");
attribute.Show();
}
//方法
MethodInfo method = type.GetMethod("Answer");
if (method.IsDefined(typeof(CustomAttribute), true))
{
CustomAttribute attribute = (CustomAttribute)method.GetCustomAttribute(typeof(CustomAttribute), true);
Console.WriteLine($"{attribute.DTime}_{attribute.Description}_{attribute.Remark}");
attribute.Show();
}
//参数
ParameterInfo parameter = method.GetParameters()[0];
if (parameter.IsDefined(typeof(CustomAttribute), true))
{
CustomAttribute attribute = (CustomAttribute)parameter.GetCustomAttribute(typeof(CustomAttribute), true);
Console.WriteLine($"{attribute.DTime}_{attribute.Description}_{attribute.Remark}");
attribute.Show();
}
//返回参数
ParameterInfo returnParameter = method.ReturnParameter;
if (returnParameter.IsDefined(typeof(CustomAttribute), true))
{
CustomAttribute attribute = (CustomAttribute)returnParameter.GetCustomAttribute(typeof(CustomAttribute), true);
Console.WriteLine($"{attribute.DTime}_{attribute.Description}_{attribute.Remark}");
attribute.Show();
}
}
}
}