参考:
https://msdn.microsoft.com/zh-cn/library/e8kc3626(v=vs.110)
http://www.runoob.com/csharp/csharp-attribute.html
特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号[]来描述的。
特性可以放置在几乎所有的声明中。在 C# 中,特性的指定方法为:将括在方括号中的特性名置于其应用到的实体的声明上方。如下代码:
[System.Serializable]
public class SampleClass
{
// Objects of this type can be serialized.
}
一个声明上可放置多个特性:
using System.Runtime.InteropServices;
static void MethodA([In][Out] ref double x)
{
}
static void MethodB([Out][In] double x)
{
}
static void MethodC([Out, In]double x)
{
}
某些特性对于给定实体可以指定多次,如, ConditionalAttribute就是一个可以多次使用的特性:
[Conditional("DEBUG"), Conditional("TEST1")]
void TraceMethod()
{
// ...
}
特性参数:
定位参数(特性类的构造函数的参数)、属性参数(特性类中的属性),也叫命名参数定位参数是必填的且必须按顺序指定, 属性参数是可选的且可以按任意顺序指定。定位参数必须在前。
[DllImport("user32.dll")]
[DllImport("user32.dll", SetLastError=false, ExactSpelling=false)]
[DllImport("user32.dll", ExactSpelling=false, SetLastError=false)]
第一个参数(DLL 名称)是定位参数并且总是第一个出现,其他参数为命名参数。在这种情况下,两个命名参数均默认为 false,因此可将其省略。
接下来看看如何使用自定义特性:
首先我们需要从System.Attribute中派生我们自己的特性类:
public class MyAttribute : Attribute
{
}
接下来就可以用自定义的特性装饰我们已有的类了:
[My()]
public class MyClass
{
}
这边提一下,根据约定,所有特性名称都以单词“Attribute”结束,以便将它们与“.NET Framework”中的其他项区分。但是,在代码中使用特性时,不需要指定 attribute 后缀。例如,[DllImport] 虽等效于 [DllImportAttribute],但 DllImportAttribute 才是该特性在 .NET Framework 中的实际名称。
现在我们来完善一下这个特性吧,看他的威力到底在哪里。
[My("this is MyAttribute!!!")]
public class MyClass
{
}
public class MyAttribute : Attribute
{
public MyAttribute(string str)
{
this.str = str;
}
private string str;
public string Str
{
get { return str; }
}
}
测试:
static void Main(string[] args)
{
MyAttribute myattribute = (MyAttribute)Attribute.GetCustomAttribute(typeof(MyClass), typeof(MyAttribute));
Console.WriteLine(myattribute.Str);
Console.ReadKey();
}
在上面例子中,我们MyAttribute特性添加了一个属性,并且再Main中查询它。
接下来看看怎样定义或控制特性的使用:
AttributeUsage类是预定义特性类之一,它帮助我们控制我们自己的定制特性的使用。它描述了一个定制特性如何被使用。
AttributeUsage有三个属性,我们可以把它放置在定制属性前面。第一个属性是:
* ValidOn
通过这个属性,我们能够定义定制特性应该在何种程序实体前放置。一个属性可以被放置的所有程序实体在AttributeTargets enumerator中列出。通过OR操作我们可以把若干个AttributeTargets值组合起来。
AttributrTargets有以下属性:
Assembly,Module,Class,Struct,Enum,Constructor,Method,Property,Field,Event,Interface,Parameter,Delegate。
AllowMultiple
这个属性标记了我们的定制特性能否被重复放置在同一个程序实体前多次。Inherited
我们可以使用这个属性来控制定制特性的继承规则。它标记了我们的特性能否被继承。下面让我们来做一些实际的东西。我们将会在刚才的MyAttribute特性前放置AttributeUsage特性以期待在它的帮助下控制MyAttribute特性的使用。
//表示可以定义类的特性,只允许单行,并且不可以被继承。
[AttributeUsage(AttributeTargets.Class,AllowMultiple = false,Inherited = false)]
public class MyAttribute : Attribute
{
public MyAttribute(string str)
{
this.str = str;
}
private string str;
public string Str
{
get { return str; }
}
}
看看上面例子,表示可以定义类的特性,只允许单行,并且不可以被继承,接下来来测试下:
public class MyClass
{
[My("this is MyAttribute!!!")]
public void Func()
{
Console.WriteLine("Func");
}
}
[AttributeUsage(AttributeTargets.Class,AllowMultiple = false,Inherited = false)]
public class MyAttribute : Attribute
{
public MyAttribute(string str)
{
this.str = str;
}
private string str;
public string Str
{
get { return str; }
}
}
看看上面例子,我们将AttributeTargets设置为了Class,并且在Func方法上面添加了特性,这是不被允许的,抛出了异常,除非我们将AttributeTargets设置为Method。接下来我们来看看AllowMultiple:
[My("this is MyAttribute!!!")]
[My("this is MyAttribute!!!")]
public class MyClass
{
public void Func()
{
Console.WriteLine("Func");
}
}
[AttributeUsage(AttributeTargets.Class,AllowMultiple = false,Inherited = false)]
public class MyAttribute : Attribute
{
public MyAttribute(string str)
{
this.str = str;
}
private string str;
public string Str
{
get { return str; }
}
}
看上面这个例子,我们在MyClass上面添加了两行特性,显而易见抛出了异常。除非我们将AllowMultiple改为true。再来看看Inherited:
using System;
using System.Diagnostics;
using System.Reflection;
namespace 特性
{
class Program
{
static void Main()
{
MyAttribute myattribute = (MyAttribute)Attribute.GetCustomAttribute(typeof(MyClass), typeof(MyAttribute));
if (myattribute != null)
Console.WriteLine(myattribute.Str);
else
Console.WriteLine("特性不存在");
MyAttribute myattribute1 = (MyAttribute)Attribute.GetCustomAttribute(typeof(NewClass), typeof(MyAttribute));
if (myattribute1 != null)
Console.WriteLine(myattribute1.Str);
else
Console.WriteLine("特性不存在");
Console.ReadKey();
}
[My("this is MyAttribute!!!")]
public class MyClass
{
public void Func()
{
Console.WriteLine("Func");
}
}
public class NewClass : MyClass
{
public void NewFunc()
{
Console.WriteLine("NewFunc");
}
}
[AttributeUsage(AttributeTargets.Class,AllowMultiple = false,Inherited = false)]
public class MyAttribute : Attribute
{
public MyAttribute(string str)
{
this.str = str;
}
private string str;
public string Str
{
get { return str; }
}
}
}
}
很显然输出:
"this is MyAttribute!!!"
特性不存在
除非我们将Inherited设置为true。
好了,大概也就这些了,最后我们在看看一个例子,很简单,只要理解了上面所讲的,基本看看代码就会懂了
using System;
using System.Diagnostics;
namespace 特性
{
class Program
{
static void Main(string[] args)
{
GetAttributeInfo(typeof(OldClass));
Console.WriteLine("==============");
GetAttributeInfo(typeof(NewClass));
Console.ReadKey();
}
public static void GetAttributeInfo(Type t)
{
OldAttribute myattribute = (OldAttribute) Attribute.GetCustomAttribute(t, typeof (OldAttribute));
if (myattribute == null)
{
Console.WriteLine(t.ToString() + "类中自定义特性不存在!");
}
else
{
Console.WriteLine("特性描述:{0}\n加入事件{1}", myattribute.Discretion, myattribute.date);
}
}
}
[AttributeUsage(AttributeTargets.Class, Inherited = false)]//设置了定位参数和命名参数
//该特性适用于所有的类,而且是非继承的。
class OldAttribute : Attribute//继承自Attribute
{
private string discretion;
public string Discretion
{
get { return discretion; }
set { discretion = value; }
}
public DateTime date;
public OldAttribute(string discretion)
{
this.discretion = discretion;
date = DateTime.Now;
}
}
//现在我们定义两类
[Old("这个类将过期")]//使用定义的新特性
class OldClass
{
public void OldTest()
{
Console.WriteLine("测试特性");
}
}
class NewClass : OldClass
{
public void NewTest()
{
Console.WriteLine("测试特性的继承");
}
}
//我们写一个方法用来获取特性信息
}
好了,祝进步。
每天进步一点点。