简介

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

特性具有以下属性:

  • 特性可向程序中添加元数据。 元数据是有关在程序中定义的类型的信息。 所有的 .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. dotnet use regex two samples

    One sample is used to replace double quote from words which encapsulated by csvwriter , you know csv ...

  2. [编]IoT The Internet of Things (IoT) 物联网

    物联网是新一代信息技术的重要组成部分.其英文名称是“The Internet of things”.由此,顾名思义,“物联网就是物物相连的互联网”.这有两层意思:第一,物联网的核心和基础仍然是互联网, ...

  3. 【MVC 4】6.SportsSore:导航

     作者:[美]Adam Freeman      来源:<精通ASP.NET MVC 4> 前面的文章[MVC 4]5.SportsSore —— 一个真实的应用程序 建立了 Sports ...

  4. NGUI:HUD Text(头顶伤害漂浮文字)

    HUD Text 很早之前就有闻于NGUI中的HUD Text插件,今天得以尝试,看了会儿官方的文档,楞是没给看明白,官方的ReadMe.txt写的使用方法如下: 官网Usage 1. Attach ...

  5. mac在xampp下使用yii2.0开发环境配置

    在mac上装环境,折腾了我好久.先用是mac自带的php,但自带的PHP很多扩展都需要自己安装.libevent,memcache等扩展都安装好了之后,发现pdo_mysql.dll扩展又没有,悲剧的 ...

  6. 线程操作案例--生产者与消费者,Object类对线程的支持

    本章目标 1)加深对线程同步的理解 2)了解Object类中对线程的支持方法. 实例 生产者不断生产,消费者不断消费产品. 生产者生产信息后将其放到一个区域中,之后消费者从区域中取出数据. 既然生产的 ...

  7. mysqli事务处理demo

    <?php  $mysqli=new mysqli("localhost", "root", "123456", "xsph ...

  8. shell+curl监控网站页面(域名访问状态),并利用sedemail发送邮件

    应领导要求,对公司几个主要站点的域名访问情况进行监控.下面分享一个监控脚本,并利用sendemail进行邮件发送. 监控脚本如下:下面是写了一个多线程的网站状态检测脚本,直接从文件中读出站点地址,然后 ...

  9. bootstrap和jquery mobile的对比

    最近一直在研究bootstrap这东西,确实是个好的框架,但是诸多优势背后也隐藏着一些不好的地方,对此,我把它和另一套响应式框架jquery mobile做了一下对比,我的总结如下:    1.boo ...

  10. 那些OVER的封装

    什么over什么,如pppoe, ppp的封装都在over对象之后,入下图: PPPOE   Ipsec