C# 中特性(Attribute)的使用简介

Attribute(特性)
MSDN给出的定义:
  Attribute 类将预定义的系统信息或用户定义的自定义信息与目标元素相关联。目标元素可以是程序集、类、构造函数、委托、枚举、事件、字段、接口、方法、可移植可执行文件模块、参数、属性 (Property)、返回值、结构或其他特性(Attribute)。
在.Net程序中,可以使用特性(Attribute)来解决许多问题。如:将WebService中接口函数标记为WebMethod,将类标记为可序列化等等。

  此外,我们也可以自定义Attribute,来实现我们需要的功能。但自定义Attribute必须继承自Attribute类。Attribute中有很多方法或者属性,为我们提供了强大的功能。一般我们通过反射的方式来使用Attribute。

下面结合实际项目中的使用谈谈自己的体会。
需求:项目中有一个专门的日志库。现在项目中需要前端用到查询、翻页、按各个字段排序等等功能。由于日志数据可能有无效的数据,并且数据量较大。为了提升统计、翻页性能,所以建立了许多临时表。如果在实际操作中为一个一个不同的需求去建立临时表,造成代码大量重复臃肿,并且不便于统一管理,考虑到的方案是通过Attribute的方式,统一建临时表。
主体思路如下:通过自定义特性来描述要建立临时表的字段的相关类型、长度等等信息,然后通过反射的方式获取这些字段对应的这些特性,然后构造相应的SQL命令就可以完成临时表的建立了。
特性定义如下:

[AttributeUsage(AttributeTargets.Property)]
public class TableFieldAttribute : Attribute
{
/// <summary>
/// 字段长度
/// </summary>
public int Length { get; set; }

/// <summary>
/// 字段描述
/// </summary>
public string Describe { get; set; }

/// <summary>
/// 字段类型
/// </summary>
public string Type { get; set; }

}

  

以上AttributeUsage,也是一个继承自Attribute的类,他的作用就是标志自定义类的使用范围,如:字段,属性,类,方法。以及使用我们自定义类
标记的类的子类是否继承特性等。
有了这个自定义特性,将它运用到实体上就行了。

public class TableEntity
{
[TableField(Describe = "Guid主键", Length = 36, Type = "uniqueidentifier")]
public Guid Guid { get; set; }

[TableField(Describe = "名称", Length = 36, Type = "varchar ")]
public string Name { get; set; }
}

  

这样在实体定义上,我们就已经有了定义好了数据类型,长度等等。在实际要建临时表时,通过反射要建临时表的实体,便能构造相应的SQL命令了。

工具类的定义:public class Tools<T> where T : class。实际使用时,将泛型类型换成相应的类型即可。
主要给出如何使用反射来获取临时表具体字段的信息代码:

public static List<Record> GetTableFieldsInformation()
{
Type t = typeof(T);
PropertyInfo[] propertyInfos = t.GetProperties();
List<Record> tableEntities = new List<Record>();
propertyInfos.ToList().ForEach(property =>
{
IList<CustomAttributeData> list = property.GetCustomAttributesData();
if (list.Count == 1)
{
tableEntities.Add(GetTableEntity(list[0].NamedArguments, property));
}
else
{
throw new Exception();
}
});
return tableEntities;
}

  


这样, GetTableFieldsInformation()方法中返回的List就是包含了临时表一个字段的所有信息的列表。我们循环列表,构造Sql命令
主要代码如下:

recordEntities.ForEach(record =>
{
if (i == 0)
{
sql = "CREATE TABLE [" + record.TableName + "](";
}
if (list.All(item => !record.Type.Equals(item, StringComparison.OrdinalIgnoreCase)))
{
sql += string.Format("{0} {1}({2}),", record.FieldName, record.Type, record.Length);
}
else
{
sql += string.Format("{0} {1},", record.FieldName, record.Type);
}
i++;
});

  

程序最终执行后如下图:


主要的思想就是这些。另外,如果你细心,你会发现AttributeUsage特性使用和我定义的TableFieldAttribute使用方式上有写不一样。
TableFieldAttribute的使用方式:[TableField(Describe = "Guid主键", Length = 36, Type = "uniqueidentifier")]
AttributeUsage的使用方式:[AttributeUsage(AttributeTargets.Property)]。
同样是自定义的特性,为什么我们定义的和Framework库里的使用不一样呢。?通过Reflect看看AttributeUsage的源码,如下图:

从图中标记的地方来看,是由于在AttributeUsage中属性的定义【 AttributeTargets ValidOn】以及构造函数的定义不一样导致的。
有兴趣深入研究的同学可以自己试试。

后记:特性的使用是很强大的一项功能。本例中使用的仅仅是其他很小的一部分。因此只对在实际应用中做了写说明。另:代码是没有经过细致整理

代码下载:https://files.cnblogs.com/tyb1222/Attributes.rar

posted @ 2011-10-24 17:00  tyb1222  阅读(3480)  评论(3编辑  收藏  举报