1、引言
attribute是.NET框架引入的一大技术亮点,因此我们有必要花点时间走进一个发现attribute登堂入室的入口。因为.NET Framework中使用了大量的定制特性来完成代码约定,[Serializable]、[Flags]、[DLLImport]、[AttributeUsage]这些构造,相信大家都见过吧(MVC中也有很多地方用到了特性),那么你是否了解其背后的技术。
提起特性,不免让人想起“属性”。特性和属性往往给初学者混淆的概念冲击,本文将对二者的概念、区别、用法、示例做以概括性的总结和比较。另外本文的主题以特性介绍为主,属性的论述重点突出在二者的比较上。
2、概念引入
2.1 什么是特性?
MADN的定义:公共语言运行时允许添加类似关键字的描述声明,叫做attribute,它对程序中的元素进行标注,如类型、字段、方法和属性等。Attribute和Microsoft .Net Framework文件的元数据(metadata)保存在一起,可以用来向运行时描述你的代码,或者在程序运行时影响程序的行为。
我们简单的总结为:定制特性attribute本质上是一个类,其为目标元素提供关联附加信息,并在运行期以反射的方式来获取附加信息。具体的特性实现方法,在接下来的讨论中继续深入。
2.2 什么是属性?
属性是面向对象编程的基本概念,提供了对私有化字段的访问封装,在c#中以get和set访问器方法实现对可读可写属性的操作,提供了安全和灵活的数据访问封装。以下是属性的简单示例:
class User
{
private string name;
public string Name
{
get { return (name == null) ? string.Empty : name; }
set { name = value; }
}
private int age;
public int Age
{
get { return age; }
set
{
if (value > 0 && value < 160)
{
age = value;
}
else
{
throw new Exception("Not a valid age");
}
}
}
}
class Program
{
static void Main(string[] args)
{
User user = new User();
user.Name = "蝈蝈";
Console.WriteLine(user.Name);
user.Age = 18;
Console.WriteLine(user.Age);
Console.ReadLine();
}
}
2.3 区别和比较
通过对概念的澄清和历史的回溯,我们发现特性和属性只是在名称上有点瓜葛,在功能和应用上,二者其实没有太多模糊的概念交叉,因此没必要来比较其应用的异同点。本文则以特性的概念为重点,来讨论其应用的场合和规则。
3、通用规则
定制特性可以应用的目标元素为:程序集(assembly)、模块(module)、类型(type)、属性(property)、事件(event)、字段(field)、方法(method)、参数(param)、返回值(return),应该全了。
定制特性以[,]形式展现,放在目标元素之上,多个特性可以应用于同一元素,特性之间以逗号隔开,以下表达规则有效:[AttributeUsage][Flags]、[AttributeUsage,Flags]、[AttributeUsageAttribute,Flags]、[Flags,AttributeUsageAttribute]、[AttributeUsage(),Flags()]。
自定义特性类型必须直接或间接继承自System.Attribute类,而且该类型必须有公有构造函数来创建其实例。
所有自定义的特性名称都应该有Atrribute后缀,这是约定。
- attribute实例在编译期进行初始化,而不是运行期。
- C#允许以指定的前缀来表示特性所应用的目标元素,建议这样来处理,因为显示处理可以消除可能带来的二义性。例如:
using System;
namespace TestAttribute
{
[assembly:MyAttribute(1)]//应用于程序集
[module:MyAttribute(2)]//应用于模块
public class User()
{......
}
}
- 定制特性可以用在其他定制特性上,因为定制特性本来就是一个类,遵守类的公有规则。例如很多时候我们的自定义定制特性会应用AttributeUsage特性,来控制如何应用新定义的特性。
[AttributeUsage(AttributeTarget.All),AllowMultiple=true,Inherited=true]
class MyAttribute:System.Attribute
{......
}
- 定制特性不会影响应用元素的任何功能,只是约定了该元素具有的特质。
- 所有非抽象特性必须具有public访问限制。
- 特性常用于编译器指令,突破#define、#undefine、#if、#endif的限制,而且更加灵活。
- 定制特性常用于在运行期获得代码注释信息,以附加信息来优化调试。
- 定制特性可以应用在某些设计模式中,如工厂模式。
- 定制特性还常应用于位标记、非托管函数标记、方法废弃标记等方面。
4、特性的应用
4.1 常用特性
事实上.NET框架已经提供了丰富的固有特性由我们发挥,以下精选出我认为最常用、最典型的固有特性做以简单讨论,从.NET提供的经典开始,或许是一种求知的捷径,希望能给大家以启示。
- AttributeUsage
- Flags
- DllImport
- Serializable
- Conditional
- Description
- DefaultValue
- Category
- ReadOnly
4.2 自定义特性
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method)]
public class VersionAttribute : System.Attribute
{
public string Name { get; set; }
public string Date { get; set; }
public string Description { get; set; }
public void Show()
{
Console.WriteLine("名称:{0} 日期:{1} 描述:{2}", Name, Date, Description);
}
}
[Version(Name="版本检测",Date="2016-9-18",Description="刚会用来一发")]
public class MyClass
{
}
static void Main(string[] args)
{
Type tp = typeof(MyClass);
VersionAttribute version = (VersionAttribute)Attribute.GetCustomAttribute(tp, typeof(VersionAttribute));
Console.WriteLine("Name=" + version.Name);
Console.WriteLine("Date=" + version.Date);
Console.WriteLine("Description=" + version.Description);
version.Show();
Console.ReadKey();
}
Attribute类是在编译的时候被实例化的,而不是像普通类那样在运行的时候才实例化,所以在Main()方法中可以在没有实例化MyClass对象的情况下获取到MyClass的VersionAttribute信息。由于Attribute类是在编译的时候被实例化的,所以你还可以用外部工具维护这些Attribute信息。
5、经典示例
5.1 小菜一碟
using System;
using System.Reflection;
static void Main(string[] args)
{
Type tp = typeof(MyClass);
VersionAttribute version = (VersionAttribute)Attribute.GetCustomAttribute(tp, typeof(VersionAttribute));
Console.WriteLine("Name=" + version.Name);
Console.WriteLine("Date=" + version.Date);
Console.WriteLine("Description=" + version.Description);
version.Show();
tp = version.GetType();
/*利用反射逐个获取属性值*/
PropertyInfo namePI = tp.GetProperty("Name");
PropertyInfo datePI = tp.GetProperty("Date");
PropertyInfo descriptionPI = tp.GetProperty("Description");
MethodInfo showMI = tp.GetMethod("Show");
string name = namePI.GetValue(version, null).ToString();
string date = datePI.GetValue(version, null).ToString();
string description = descriptionPI.GetValue(version, null).ToString();
Console.WriteLine("-------------------------");
Console.WriteLine("Name=" + name);
Console.WriteLine("Date=" + date);
Console.WriteLine("Description=" + description);
showMI.Invoke(version, null);
Console.ReadKey();
}
5.2 他山之石
MSDN认为,特性(Attribute)描述如何将数据序列化,指定用于强制安全性的特性,并限制实时(JIT)编译器的优化,从而使代码易于调试。特性(Attribute)还可以记录文件名或代码作者,或在窗体开发阶段控制控件或成员的可见性。
dudu收藏的系列文章《Attribute在.net编程中的应用》,给你应用方面的启示会很多值得研究。http://www.cnblogs.com/dudu/articles/4451.html
亚历山大同志的系列文章《手把手教你写ORM(六)》中也有很好的诠释。http://www.cnblogs.com/Alexander-Lee/archive/2007/01/24/hbh-orm-06.html
idior的文章《Remoting基本原理及其扩展机制》也很有用,因此补充。http://www.cnblogs.com/idior/archive/2007/03/06/665479.html
6、结论
Attribute是.net引入的一大技术特色,现在应用已经非常广泛,MVC、jquery插件等都有特性应用的出现。更深层次的应用,例如序列化、程序安全性、设计模式等多方面都可以挖掘出闪耀的金子,有需要的同学可以继续研究。