简介

特性提供功能强大的方法,用以将元数据或声明信息与代码(程序集、类型、方法、属性等)相关联。 特性与程序实体关联后,即可在运行时使用名为“反射”的技术查询特性。

特性具有以下属性:

  • 特性可向程序中添加元数据。 元数据是有关在程序中定义的类型的信息。 所有的 .NET 程序集都包含指定的一组元数据,这些元数据描述在程序集中定义的类型和类型成员。 可以添加自定义特性,以指定所需的任何附加信息。

  • 可以将一个或多个特性应用到整个程序集、模块或较小的程序元素(如类和属性)。

  • 特性可以与方法和属性相同的方式接受参数。

  • 程序可以使用反射检查自己的元数据或其他程序内的元数据。

(以上来自MSDN)

特性的定义

首先看一个例子,通过该例子可以更好地说明怎样使用特性这个具有强大功能的特征。假如您有一个在Windows注册表里存储信息的程序。那么您要解决的一个设计难题是在何处存储注册表的键值信息。在大多的开发环境中,一种典型的方式就是把该信息存储在一个资源文件里, 或者存在常量里,或者把该信息牢固地编写到Registry API的调用中。但是,这种方式会把原本是一个整体的类与该类的定义部分分开来存储。如果使用特性,我们就可以把这部分信息附在类成员上,这样的话,我们就有一个完全自我描述的组件了、下面通过例子看看这些是如何实现的,假定已经在其它地方定义了一个叫做ResgistryKey的特性。

 class MyClass
{
[RegistryKey(HKEY_CURRENT_USER,"foo")]
public int Foo;
}

要给一个c#类型或成员附加一个已定义的特性,只要在目标类型或成员的前面加上特性数据,并用括号把它们括起来即可。例如上面的代码中我们给MyClass.Foo字段加了一个名为RegistryKey的特性。在运行时我们就可以立刻看到---对于注册表键值我们要做的就是查询这个字段,然后用这个值把日期保存到注册表里。

在上面的例子中,您会发现给一个类型或成员附加特性的语法看起来有点像类的实例化。这是因为一个特性确切的说就是一个从System.Attribute基类派生的类。现在让我们看一看RegistryKey特性的结构。

  public enum RegistryHives
{
HKEY_CLASSES_ROOT,
HKEY_CURRENT_USER,
HKEY_LOCAL_MACHINE,
HKEY_USERS,
HKEY_CURRENT_CONFIG
}
public class RegistryKeyAttribute : Attribute
{
public RegistryKeyAttribute(RegistryHives Hive, string valueName)
{
this.valueName = valueName;
this.Hive = Hive;
}
private RegistryHives hive; public RegistryHives Hive
{
get { return hive; }
set { hive = value; }
}
private string valueName; public string ValueName
{
get { return valueName; }
set { valueName = value; }
}
}

我们在这里加了一个enum来声明一个不同的Registry类型,它有一个特性类的构造函数(带有一个Registry类型的参数和一个值参数),两个属性(Registry配置单元和值名)。您可以有很多方式来定义特性,现在我们知道了怎样定义和附加特性,那么怎么在运行时查询特性呢?下面通过一个完整的例子来开始我们的工作,一旦完成这部分,我们就可以继续处理一些定义和附加特性的更高级的问题。

注意:注意在这个例子中特性类名后附加了Attributue。但是,当在以后把这个特性附加给一个类型或成员时,不需要再包括这个Attribute后缀了。这是C#语言设计员们提供的一种快捷方式。当编译器看到一个特性附加给一个类型或成员时,它就会搜索带有制定名称的System.Attribute的派生类。如果没有找到这样的类,编译器就会先给这个特性加上Attribute,然后再搜索这个特性。因此,通常的做法是:定义特性类名时以Attribute结尾,之后常常省略Attribute这部分。

对特性进行查询

我们已经知道怎样来通过System.Attribute派生类定义一个特性,以及怎样把它附加给一个类型成员。那么在代码中我们怎样使用特性呢?换句话说,我们怎么样才能根据所附加的特性(和它的参数)来查询一个类型或一个成员呢?

要查询一个类型或者成员的附加特性,我们需要用到反射(refection)。反射就是这样一种特征:可以在运行时动态地决定应用程序的类型特点。

  1. 类的特性

怎样获得一个特性取决于您要查询的成员类型。假如您要定义一个特性,这个特性被用来定义创建对象的远程服务器。不使用特性的话,您就要把这个信息存储在一个常量里或者是应用程序的资源文件里。使用特性的话,您只需要简单地(像下面这样)为这个类添加一个远程服务器的注释即可。

     public enum RemoteServers
{
JEANVALJEAN,
JAVERT,
COSETTE
}
public class RemoteObjectAttribute : Attribute
{
public RemoteObjectAttribute(RemoteServers server)
{
this.server = server;
}
private RemoteServers server; public RemoteServers Server
{
get { return server; }
set { server = value; }
}
}
     [RemoteObject(RemoteServers.COSETTE)]
class MyRemotableClass
{ }

要确定创建的远程服务器,可以使用如下代码:

         static void Main(string[] args)
{
Type type=typeof(MyRemotableClass);
foreach (Attribute attr in type.GetCustomAttributes(false))
{
RemoteObjectAttribute remoteAttr = attr as RemoteObjectAttribute;
if (remoteAttr!=null)
{
Console.WriteLine("Create this object on "+remoteAttr.Server);
}
}
Console.Read();
}

应用程序输出结果为:

上面的例子很有代表性,我们可以通过它来测试一下反射是怎样工作的,以及运行时是怎样返回特性的值的。您会注意到主程序的第一行试用了typeof操作符。

 Type type=typeof(MyRemotableClass);

这个操作符返回和这个作为它唯一的参数值的类型相关的System.Type对象。一旦您得到这个对象,您就可以对他进行查询

 foreach (Attribute attr in type.GetCustomAttributes(false))

Type.GetcustomAttribute方法的调用。这个方法返回Attribute类型的数组,这个数组包含所有的附加给名为MyRemoteableClass类的特性。

         //
// 摘要:
// 在派生类中重写时,返回应用于此成员的所有自定义特性的数组。
//
// 参数:
// inherit:
// 搜索此成员的继承链以查找这些属性,则为 true;否则为 false。 属性和事件中忽略此参数,请参见“备注”。
//
// 返回结果:
// 一个包含应用于此成员的所有自定义特性的数组,在未定义任何特性时为包含零个元素的数组。
//
// 异常:
// System.InvalidOperationException:
// 该成员属于加载到仅反射上下文的类型。 请参见如何:将程序集加载到仅反射上下文中。
//
// System.TypeLoadException:
// 未能加载自定义特性类型。
public abstract object[] GetCustomAttributes(bool inherit);
//
// 摘要:
// 在派生类中重写时,返回应用于此成员并由 System.Type 标识的自定义特性的数组。
//
// 参数:
// attributeType:
// 要搜索的特性类型。 只返回可分配给此类型的属性。
//
// inherit:
// 搜索此成员的继承链以查找这些属性,则为 true;否则为 false。 属性和事件中忽略此参数,请参见“备注”。
//
// 返回结果:
// 应用于此成员的自定义特性的数组;如果未应用任何可分配给 attributeType 的特性,则为包含零个元素的数组。
//
// 异常:
// System.TypeLoadException:
// 无法加载自定义特性类型。
//
// System.ArgumentNullException:
// 如果 attributeType 为 null。
//
// System.InvalidOperationException:
// 该成员属于加载到仅反射上下文的类型。 请参见如何:将程序集加载到仅反射上下文中。
public abstract object[] GetCustomAttributes(Type attributeType, bool inherit);

GetCustomAttributes

2.字段的特性

假如有一个包含一些字段的类,您想把这些字段的值存储在注册表中。要想这样,您可以利用构造函数来定义一个特性,并且使该构造函数取两个参数,一个为枚举类型,代表正确的注册表配置单元,一个为字符串类型,代表注册表的值名。下面您就可以根据字段的注册键值在运行时进行字段的查询了。

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Wolfy.AttributeDemo
{
public enum RegistryHives
{
HKEY_CLASSES_ROOT,
HKEY_CURRENT_USER,
HKEY_LOCAL_MACHINE,
HKEY_USERS,
HKEY_CURRENT_CONFIG
}
public class RegistryKeyAttribute : Attribute
{
public RegistryKeyAttribute(RegistryHives Hive, string valueName)
{
this.valueName = valueName;
this.Hive = Hive;
}
private RegistryHives hive; public RegistryHives Hive
{
get { return hive; }
set { hive = value; }
}
private string valueName; public string ValueName
{
get { return valueName; }
set { valueName = value; }
}
} }

RegistryKeyAttribute

 public class TestClass
{
[RegistryKey(RegistryHives. HKEY_CURRENT_USER,"Foo")]
public string Foo;
public string Bar;
}

TestClass

控制台程序:

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks; namespace Wolfy.AttributeDemo
{
class Program
{
static void Main(string[] args)
{
Type type = typeof(TestClass);
foreach (FieldInfo field in type.GetFields())
{
foreach (Attribute attr in field.GetCustomAttributes())
{
RegistryKeyAttribute registryKeyAttr = attr as RegistryKeyAttribute;
if (registryKeyAttr != null)
{
Console.WriteLine("{0} will be saved in {1} \\\\{2}", field.Name, registryKeyAttr.Hive, registryKeyAttr.ValueName);
}
}
}
Console.Read();
}
}
}

结果:

结语

本文来自《c#技术内幕》,觉得本章节内容还是比较容易理解的,不敢独享,特分享在此,希望能帮到别人。未完待续.......

[C#]Attribute特性的更多相关文章

  1. .net学习之Attribute特性和EF关键知识点

    一.Attribute特性/标签1.Attribute用来对类.属性.方法等标注额外的信息,贴一个标签简单的说,定制特性Attribute,本质上就是一个类,它为目标元素提供关联附加信息,并在运行时以 ...

  2. [C#]Attribute特性(3)——AttributeUsage特性和特性标识符

    相关文章   [C#]Attribute特性 [C#]Attribute特性(2)——方法的特性及特性参数 AttributeUsage特性 除了可以定制自己的特性来注释常用的C#类型外,您可以用At ...

  3. [C#]Attribute特性(2)——方法的特性及特性参数

    上篇博文[C#]Attribute特性介绍了特性的定义,类的特性,字段的特性,这篇博文将介绍方法的特性及特性参数相关概念. 3.方法的特性 之所以将这部分单列出来进行讨论,是因为对方法的特性查询的反射 ...

  4. 关于C# 中的Attribute 特性

    关于C# 中的Attribute 特性 作者: 钢钢  来源: 博客园  发布时间: 2011-01-09 23:30  阅读: 13921 次  推荐: 12   原文链接 [收藏] 摘要:纠结地说 ...

  5. Attribute特性验证模型model

    数据验证我们往往分为前台验证和后台验证,而我们的后台验证每到一个方法中就要去验证一次,这样的代码想想都难以维护,这篇我们这篇文章就是为了解决这样的问题.用attribute 这个特性来解决这样的问题 ...

  6. 如何在方法上贴上attribute(特性)捕捉方法的异常,来实现我们的需求

    在方法上贴上attribute(特性)捕捉方法的异常,其实这么做也是为了在项目中不会大量使用try-cacth这样的语句,同时使我们的代码看起来更简洁,更直观,将逻辑业务分离使得后期维护方便.这里我们 ...

  7. .NET进阶篇03-Reflection反射、Attribute特性

    知识需要不断积累.总结和沉淀,思考和写作是成长的催化剂 内容目录 一.概述二.反射1.反射使用2.创建对象3.调用方法4.字段属性三.特性四.总结 一.概述 反射其实无处不在,我们用VS进行调试时候, ...

  8. Net中Attribute特性的高级使用及自定义验证实现

    好久没写博客了,今天在百忙之中抽空来写篇文章,记录一下最近深入学习Attribute特性的笔记及心得.~~ 一.什么是特性? 特性(Attribute)是用于在运行时传递程序中各种元素(比如类.方法. ...

  9. C#基础系列——Attribute特性使用

    前言:上篇 C#基础系列——反射笔记 总结了下反射得基础用法,这章我们来看看C#的另一个基础技术——特性. 1.什么是特性:就博主的理解,特性就是在类的类名称.属性.方法等上面加一个标记,使这些类.属 ...

随机推荐

  1. HTML5 datalist 标签

    以前需要用JS写一个自动完成组件(Suggest),很费劲.HTML5时代则不用了,直接使用datalist标签,直接减少了工作量.如下 <!DOCTYPE html> <html& ...

  2. JavaScript中点号“.”的多义性

    点号「.」在JavaScript中有两种语义 语义1.表示算术中的小数点(浮点数),如 2.5 语义2.取对象属性.方法,如 [].push(2) 这几乎没有任何难理解的地方,但下面这个问题则很有趣. ...

  3. 字符串_KMP算法(求next[]模板 hdu 1711)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1711 问题描述:给两个序列a,b,长度分别为n,m(1<=n<=1000000,1< ...

  4. 【node.js】安装express后,'express' 不是内部或外部命令的问题

    因express默认安装是最新的版本,已经是4.x.x的版本.而最新express4.0+版本中将命令工具分出来了,所以必须要安装express-generator,执行: D:\TOOLS\Node ...

  5. 19 图形用户界面编程 - 《Python 核心编程》

  6. [转帖]迅为4412开发板最小linux系统的存储空间修改

    本文转自迅为论坛:http://www.topeetboard.com 最小linux系统的存储空间修改以修改成 1G 存储空间为例来修改,如果需要改成其他大小的存储空间,参照此方法修改即可. 首先连 ...

  7. 图论 公约数 找环和链 BZOJ [NOI2008 假面舞会]

    BZOJ 1064: [Noi2008]假面舞会 Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 1655  Solved: 798[Submit][S ...

  8. Volley(五)—— 自定义Request

    详细解读Volley(四)—— 自定义Request Volley中提供了几个Request,如果我们有特殊的需求,完全可以自定义Request的,自定义Request自然要继承Request,那么本 ...

  9. js知识体系的梳理一

    今天简单的总结了js的一些东西,梳理下整个体系,每一次的总结都会有不同的收获:js总结一一.[获取元素]: 1.通过ID: var oBtn=document.getElementById('btn1 ...

  10. for循环的三种写法

    第一种写法  传统的方法,遍历数组 String[] arr = { "amy", "heinrich", "cindy", "g ...