**特性(Attribute)**是用于再运行时传递程序仲各种元素(比如类、方法、结构枚举、组件等)的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置再它所应用的元素前面的方括号([])来描述的。
特性用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息。
.Net框架提供了两种类型的特性:预定义特性和 自定义特性
预定义特性
.Net框架提供了三种预定义特性:
- AtrributeUsage
- Conditional
- Obsolete
AtrributeUsage
AttributeUsage有三个属性,我们可以把它放置在定制属性前面。第一个属性是:
- ValidOn
通过这个属性,我们能够定义定制特性应该在何种程序实体前放置。一个属性可以被放置的所有程序实体在AttributeTargets enumerator中列出。通过OR操作我们可以把若干个AttributeTargets值组合起来。 - AllowMultiple
这个属性标记了我们的定制特性能否被重复放置在同一个程序实体前多次。 - Inherited
我们可以使用这个属性来控制定制特性的继承规则。它标记了我们的特性能否被继承。
C# AttributeUsage的使用实例:
我们将会在Help特性前放置AttributeUsage特性以期待在它的帮助下控制Help特性的使用。
先让我们来看一下AttributeTargets.Class。它规定了Help特性只能被放在class的前面。这也就意味着下面的代码将会产生错误:
编译器报告错误如下:
我们可以使用AttributeTargets.All来允许Help特性被放置在任何程序实体前。可能的值是:
Assembly,
Module,
Class,
Struct,
Enum,
Constructor,
Method,
Property,
Field,
Event,
Interface,
Parameter,
Delegate,
All = Assembly | Module | Class | Struct | Enum | Constructor | Method | Property | Field | Event | Interface | Parameter | Delegate,
ClassMembers = Class | Struct | Enum | Constructor | Method | Property | Field | Event | Delegate | Interface
使用AttributeTargets.All
后,编译器错误消失
AllowMultiple =false时,
不允许重复使用。
C# AttributeUsage的使用会有四种可能的组合:
[AttributeUsage(AttributeTargets.Class,
AllowMultiple = false, Inherited = false ]
[AttributeUsage(AttributeTargets.Class,
AllowMultiple = true, Inherited = false ]
[AttributeUsage(AttributeTargets.Class,
AllowMultiple = false, Inherited = true ]
[AttributeUsage(AttributeTargets.Class,
AllowMultiple = true, Inherited = true ]
- C# AttributeUsage的使用第一种情况:
如果我们查询(Query)Derive类,我们将会发现Help特性并不存在,因为inherited属性被设置为false。
var type = typeof(Derive);
Assert.False(type.IsDefined(typeof(HelpAttribute),true));
Assert.False(type.IsDefined(typeof(HelpAttribute), false));
-
C# AttributeUsage的使用第二种情况:
和第一种情况相同,因为inherited也被设置为false。 -
C# AttributeUsage的使用第三种情况:
对于第三种和第四种,通过类型查找都是一样的[Fact] public void AttributeTest() { var type = typeof(Derive); Assert.True(type.IsDefined(typeof(HelpAttribute),true)); Assert.False(type.IsDefined(typeof(HelpAttribute), false)); }
我们给派生类添加点代码,来解释第三种和第四种情况,:
[Help("BaseClass")]
public class Base
{
}
[Help("DeriveClass")]
public class Derive : Base
{
}
现在我们来查询一下Help特性,我们只能得到派生类的属性,因为inherited被设置为true,但是AllowMultiple却被设置为false。因此基类的Help特性被派生类Help特性覆盖了。
var type = typeof(Derive);
//Assert.True(type.IsDefined(typeof(HelpAttribute),true));
//Assert.False(type.IsDefined(typeof(HelpAttribute), false));
Assert.True(type.GetCustomAttributes(true).Count() == 1);
-
C# AttributeUsage的使用第四种情况:
在这里,我们将会发现派生类既有基类的Help特性,也有自己的Help特性,因为AllowMultiple被设置为true。var type = typeof(Derive); //Assert.True(type.IsDefined(typeof(HelpAttribute),true)); //Assert.False(type.IsDefined(typeof(HelpAttribute), false)); Assert.True(type.GetCustomAttributes(true).Count() > 1);
Conditional
利用 Conditional 属性,程序员可以定义条件方法。Conditional 属性通过测试条件编译符号来确定适用的条件。当运行到一个条件方法调用时,是否执行该调用,要根据出现该调用时是否已定义了此符号来确定。如果定义了此符号,则执行该调用;否则省略该调用(包括对调用的参数的计算)。
条件方法要受到以下限制:
- 条件方法必须是类声明或结构声明中的方法。如果在接口声明中的方法上指定 Conditional 属性,将出现编译时错误。
- 条件方法必须具有 void 返回类型。
- 不能用 override 修饰符标记条件方法。但是,可以用 virtual 修饰符标记条件方法。此类方法的重写方法隐含为有条件的方法,而且不能用 Conditional 属性显式标记。
- 条件方法不能是接口方法的实现。否则将发生编译时错误。
- 如果条件方法用在“委托创建表达式”中,也会发生编译时错误
PS:尽量使用Conditional属性代替#if和#endif
/*MyConditional.cs*/
//#define DEBUG
using System;
using System.Diagnostics;
class Info
{
//[Conditional("DEBUG")]
public static void Trace(string strMessage)
{
Console.WriteLine(strMessage);
}
[Conditional("DEBUG")]
public static void TraceX(string strFormat,params object[] list)
{
Console.WriteLine(strFormat, list);
}
}
class TestConditional
{
public static void Main()
{
Info.Trace("Cool!");
Info.TraceX("{0} {1} {2}","C", "U", 2001);
}
}
/*
编译方式1:
csc /define:DEBUG /out:1.exe MyConditional.cs
运行结果1:
Cool!
C U 2001
*/
/*
编译方式2:
csc /out:1.exe MyConditional.cs
运行结果2:
Cool!
*/
Obsolete
这个预定义特性标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素。例如,当一个新方法被用在一个类中,但是您仍然想要保持类中的旧方法,您可以通过显示一个应该使用新方法,而不是旧方法的消息,来把它标记为 obsolete(过时的)。
规定该特性的语法如下:
[Obsolete(
message
)]
[Obsolete(
message,
iserror
)]
- 参数 message,是一个字符串,描述项目为什么过时以及该替代使用什么。
- 参数 iserror,是一个布尔值。如果该值为 true,编译器应把该项目的使用当作一个错误。默认值是 false(编译器生成一个警告)
示例:
当您尝试编译该程序时,编译器会给出一个错误消息说明:
参数 iserror为false时:
自定义特性
参考:
https://www.cnblogs.com/Ramon-Zeng/p/10150739.html
https://www.cnblogs.com/loverwangshan/p/10153832.html