您的位置:首页 > 编程语言 > C#

C# 特性详解

2016-04-01 11:43 387 查看
  特性提供功能强大的方法,用以将元数据或声明信息与代码(程序集、类型、方法、属性等)相关联。这些元数据是在编译过程中创建,并嵌入到程序集中。特性与程序实体关联后,即可在运行时使用名为“反射”的技术查询特性。反射是个普通术语,它描述了在运行过程中检查和处理程序元素的功能。

特性具有以下属性:

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

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

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

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

元数据是什么?

  你注意过程序及编译的时候的pdb文件了吗?pdb文件里面存储了,关于程序集内部的所有的成员信息,例如,成员的数据类型,属性类型,方法返回值,方法入参类型,就是程序及内部所有的定义信息的数据信息,是存储定义信息的一类数据信息,程序集里面的所有的关于声明类的数据信息,包括方法间调用,都是存储在元数据里面。

详细查看:http://www.cnblogs.com/DswCnblog/p/5344119.html

特性可以放置在几乎所有的声明中。在 C# 中,特性的指定方法为:将括在方括号中的特性名置于其应用到的实体的声明上方,一个元素上面可以放置多个特性

[System.Serializable]
public class SampleClass
{
// Objects of this type can be serialized.
}


  根据约定,所有特性名称都以单词“Attribute”结束,以便将它们与“.NET Framework”中的其他项区分。在代码中使用特性,C#编译器会把字符串Attribute追加到这个特性名称后面,然后在其搜索路径的所有名称空间中搜索指定名称的类。如果该特性的名称已字符串Attribute结尾,编译器就不会把字符串加到组合名称中。

预定义特性(Attribute)

.Net 框架提供了三种预定义特性:

AttributeUsage

Conditional

Obsolete

AttributeUsage特性

预定义特性 AttributeUsage 主要用于标示自定义特性可以应用到哪些类型的程序元素上,这个信息由第一个参数给出。

规定该特性的语法如下:

[AttributeUsage(
  validon,               //规定特性可被放置的语言元素,它是枚举器 AttributeTargets 的值的组合,默认值是 AttributeTargets.All
  AllowMultiple=allowmultiple,   //如果为 true,则该特性可以在同一个元素多次使用,默认值是 false(不可多次使用)
  Inherited=inherited        //如果为 true,则该特性可被派生类继承,默认值是 false(不被继承)
)]


例如:

[AttributeUsage(AttributeTargets.Class |
          AttributeTargets.Constructor |
          AttributeTargets.Field |
          AttributeTargets.Method |
          AttributeTargets.Property,
          AllowMultiple = true)]


AttributeTargets :

成员名称说明
Assembly可以对程序集应用特性。
Module可以对模块应用特性。


注意
Module 指的是可移植的可执行文件(.dll 或 .exe),而非 Visual Basic 标准模块。

Class可以对类应用特性。
Struct可以对结构应用特性,即值类型。
Enum可以对枚举应用特性。
Constructor可以对构造函数应用特性。
Method可以对方法应用特性。
Property可以对属性应用特性。
Field可以对字段应用特性。
Event可以对事件应用特性。
Interface可以对接口应用特性。
Parameter可以对参数应用特性。
Delegate可以对委托应用特性。
ReturnValue可以对返回值应用特性。
GenericParameter可以对泛型参数应用特性。


注意
目前,此特性可以应用仅于 C#、Microsoft 中间语言 (MSIL) 和发出的代码。

All可以对任何应用程序元素应用特性。
按照上面的经验,再次开始动手来熟悉这一切,我指定了该自定义的特性不可继承,就在不解释别的了只是为了证明一下命名参数Inherited定性成功与否,总之还是很简单的。

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("测试特性的继承");
}
}
//我们写一个方法用来获取特性信息
}


运行效果:



Conditional

这个预定义特性标记了一个条件方法,其执行依赖于它顶的预处理标识符。

它会引起方法调用的条件编译,取决于指定的值,比如 DebugTrace。例如,当调试代码时显示变量的值。

#define DEBUG        //定义DEBUG宏,则下面有输出,不定义则没有输出
using System;
using System.Diagnostics;
public class Myclass
{
[Conditional("DEBUG")]
public static void Message(string msg)
{
Console.WriteLine(msg);
}
}

class Test
{
static void function1()
{
Myclass.Message("In Function 1.");
function2();
}
static void function2()
{
Myclass.Message("In Function 2.");
}
public static void Main()
{
Myclass.Message("In Main function.");
function1();
Console.ReadKey();
}
}


ObsoleteAttribute

  这个预定义特性标记了不应被使用的程序实体。它可以让您通知编译器丢弃某个特定的目标元素。例如,当一个新方法被用在一个类中,但是您仍然想要保持类中的旧方法,您可以通过显示一个应该使用新方法,而不是旧方法的消息,来把它标记为 obsolete(过时的)。

public ObsoleteAttribute(string message, bool error)


参数             类型:

message       System ..::.String        描述可选的变通方法的文本字符串。

 error         System ..::.Boolean       指示是否将使用已过时的元素视为错误的布尔值。

using System;
public class MyClass
{
[Obsolete("Don't use OldMethod, use NewMethod instead", true)]
static void OldMethod()
{
Console.WriteLine("It is the old method");
}
public static void Main()
{
OldMethod();      //将会报错
}
}


自定义特性

  通过定义一个特性类,该特性类直接或间接地从Attribute派生,有助于方便快捷地在元数据中标识特性定义。特性类本身用一个特性-System.AttributeUsage特性来标记,这个是Microsoft定义的特性,AttributeUsage主要用于表示自定义特性可以应用到哪些类型的程序上。这些信息由它的第一个参数给出,该参数是必选的,其类型是枚举类型AttributeTarget。

一个新的自定义特性应派生自 System.Attribute 类,例如:


// 一个自定义特性 BugFix 被赋给类及其成员
[AttributeUsage(AttributeTargets.Class |
  AttributeTargets.Constructor |
  AttributeTargets.Field |
  AttributeTargets.Method |
  AttributeTargets.Property,
  AllowMultiple = true)]

public class DeBugInfo : System.Attribute    //已经声明了一个名为 DeBugInfo 的自定义特性。


让我们构建一个名为 DeBugInfo 的自定义特性,该特性将存储调试程序获得的信息。它存储下面的信息:

bug 的代码编号

辨认该 bug 的开发人员名字

最后一次审查该代码的日期

一个存储了开发人员标记的字符串消息

我们的 DeBugInfo 类将带有三个用于存储前三个信息的私有属性(property)和一个用于存储消息的公有属性(property)。所以 bug 编号、开发人员名字和审查日期将是 DeBugInfo 类的必需的定位( positional)参数,消息将是一个可选的命名(named)参数。

每个特性必须至少有一个构造函数。必需的定位( positional)参数应通过构造函数传递。下面的代码演示了 DeBugInfo 类:

// 一个自定义特性 BugFix 被赋给类及其成员
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]

public class DeBugInfo : System.Attribute
{
private int bugNo;
private string developer;
private string lastReview;
public string message;

public DeBugInfo(int bg, string dev, string d)
{
this.bugNo = bg;
this.developer = dev;
this.lastReview = d;
}

public int BugNo
{
get
{
return bugNo;
}
}
public string Developer
{
get
{
return developer;
}
}
public string LastReview
{
get
{
return lastReview;
}
}
public string Message
{
get
{
return message;
}
set
{
message = value;
}
}
}


应用自定义特性

通过把特性放置在紧接着它的目标之前,来应用该特性:

[DeBugInfo(45, "Zara Ali", "12/8/2012", Message = "Return type mismatch")]
[DeBugInfo(49, "Nuha Ali", "10/10/2012", Message = "Unused variable")]
class Rectangle
{
// 成员变量
protected double length;
protected double width;
public Rectangle(double l, double w)
{
length = l;
width = w;
}
[DeBugInfo(55, "Zara Ali", "19/10/2012",
Message = "Return type mismatch")]
public double GetArea()
{
return length * width;
}
[DeBugInfo(56, "Zara Ali", "19/10/2012")]
public void Display()
{
Console.WriteLine("Length: {0}", length);
Console.WriteLine("Width: {0}", width);
Console.WriteLine("Area: {0}", GetArea());
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: