属性信息与反射

1.概念

应该有这样的一个概念:DotNET 程序包含有代码、数据和元数据。
元数据:(metadata)它是一种二进制信息,对公共运行库中的PE文件和内存中的程序进行描述.如果将自己的程序编译成PE文件时,便会将原数据信息插入到程序中,最终成为程序的一部分。在运行时,运行库将元数据加载入内存,根据原数据发现有关代码的类、成员、继承等信息。
元数据存储以下信息:
程序集的说明
· 标识(名称、版本、区域性、公钥)。
· 导出的类型。
· 该程序集所依赖的其他程序集。
· 运行所需的安全权限。
类型的说明。
· 名称、可见性、基类和实现的接口。
· 成员(方法、字段、属性、事件、嵌套的类型)。
属性
· 修饰类型和成员的其他说明性元素。
元数据就是一种自描述机制,运行库和程序模块不用向操作系统注册,自我描述始终说明了实际编译的代码,这样提高了应用程序的可靠性。

属性信息:是一种在程序中添加元数据的一种机制。属性信息是插入到元数据中的。
反射:就是程序读取自己元数据的的过程。程序自省就是从配件中提出元数据,并通知用户或修改自身行为。

2、属性信息

他是一个对象,表示与程序中某元素相关联的数据。其中的元素称为属性信息的目标。
可以理解为他和某个类的某个属性(Target)、方法等相关连。

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

3 属性信息分为两种

属性以两种形式存在:一种是在公共语言运行库的基类库中定义的属性,另一种是可以创建,可以向代码中添加附加信息的自定义属性。

4.属性信息的目标

如果在CLR中搜寻的话,可以找到极多属性信息。有些属性信息适用于配件,另一
些适用于类或接口,而其他属性信息(如[WebMethod])适用于类的成员。这些适
用对象称为属性信息的目标.

成员名                           用法
---------------------------------------------------------------
All                    |适用于以下元素:配件,类,类成员,代表,枚举,事件,字段,
                       |接口,方法,模块,参数,性质,返回值或结构体
------------------------------------------------------------------------------
Assembly               |适用于配件本身
-----------------------------------------------------------------------
Class                  |适用于类的实例
--------------------------------------------------------------------------
Constructor            |适用于一个给定的构造方法
------------------------------------------------------------------------------
Delegate               |适用于一个代表方法
------------------------------------------------------------------------
Enum                   |适用于一个枚举
-----------------------------------------------------------------------------
Field                  |适用于一个字段
-----------------------------------------------------------------------------
Interfale              | 适用于一个接口
-----------------------------------------------------------------------------
Method                 | 适用于一个方法
-----------------------------------------------------------------------------
Module                 |适用于一个模块
-----------------------------------------------------------------------------
Parameter              |适用于一个方法的参数
-----------------------------------------------------------------------------
Property               |适用于一个性质(包括get 和set 方法,如果实现了的话)
-----------------------------------------------------------------------------
ReturnValue            |适用于一个返回值
-----------------------------------------------------------------------------
Struct                 |适用于一个结构体
-----------------------------------------------------------------------------

5、应用属性信息

将属性信息应用于其目标时,应将属性信息放在紧贴目标之前的方括号中。我们还
可以结合使用多个属性信息,一个撂在另一个上面就行:
[assembly: AssemblyDelaySign(false)]
[assembly: AssemblyKeyFile(".//keyFile.snk")]
或用逗号分开:
[assembly: AssemblyDelaySign(false),
assembly: AssemblyKeyFile(".//keyFile.snk")]
注意: 必须将配件的属性信息放在using 语句之后,任意代码之前。

6、自定义属性信息

可以在需要的时候自己创造属性信息,并且在运行期间使用。
例如,跟踪bug的修补情况,并且有一个数据库保存缺陷,这在代码中很容易看见,但不能与数据库中的Bug323 连接。这时就该用到自定义
属性信息了。可以用以下代码替换注释:
[BugFixAttribute(323,"Jesse Liberty","1/1/2005")
Comment="Off by one error"]
然后写一个程序读取元数据,寻找缺陷弥补记号,并更新数据库。属性信息可以用
作注释,但它同时可以通过工具程序化地获取信息。

7.声明属性信息

属性信息与C# 中大多数东西一样,是以类来实现的。创造自
System.Attribute 派生新的自定义属性信息类:
public class BugFixAttribute : System.Attribute需要告诉编译器此属性信息可以被用于何种元素(属性信息目标)。这也是用一个属
性信息来指定的:
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
。AttributeUsage属性信息的构造方法有两个参数。第一个是一组表示目标的
标志(这里是类及其构造方法、字段、方法和性质)。第二个参数是一个标志,表示
元素是否可接受一个以上的该属性信息。在本例中,AllowMultiple被置为true,
表示类的成员可以指派一个以上的BugFixAttribute。
命名属性信息
本例中新的自定义属性信息名为BugFixAttribute。习惯上要在属性信息名后加
Attribute一词。编译器也支持用名称的简写调用属性性信息。因此,可以这样写:
[BugFix(123, "Jesse Liberty", "0I/OI/O5", Comment="Off by one")]
编译器首先寻找名为BugFix 的属性信息,如果没找到,会再寻找BugFix-
Attribute。
8.构造属性信息
每个属性信息必须至少有一个构造方法。属性信息有两种参数:位置型(positional)
参数和命名型(named)参数。以BugFix为例,程序员的名字(Jesse Liberty)和日期是位置型参数,而Comment是命名型参数。位置型参数将传入构造方法,且必
须以构造方法中声明的顺序传入:
public BugFixAttribute(int bugID, string programmer,
string date)
{
this.bugID = bugID;
this.programmer = programmer;
this.date = date;
}
而命名型参数则以性质形式实现:
public string Comment
{
get
{
return comment;
}
set
{
comment = value;
}
}
为位置型参数创建只读性质也很常见:
public int BugID
{
get
{
return bugID;
}
}

9.使用属性信息

定义完属性信息后,就可以把它紧贴置于目标之前,开始使用它了。要测试前一个
例中的BugFixAttribute,下列程序创建了一个名为MyMath的简单类,它有两个
方法。可以将BugFixAttribute 指派给这个类,记录其代码维护的历史。
[BugFixAttribute(121,"Jesse Liberty","0I/O3/05")]
[BugFixAttribute(107,"Jesse Liberty","0I/O4/05",
Comment="Fixed off by one errors")]
这些属性信息将同元数据一道存储。
namespace Programming_CSharp
{
using System;
using System.Reflection;
// 创建要指派给类成员的自定义属性信息
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true)]
public class BugFixAttribute : System.Attribute
{
// 位置型参数的
// 属性信息构造方法
public BugFixAttribute
(int bugID,
string programmer,
string date)
{
this.bugID = bugID;
this.programmer = programmer;
this.date = date;
}
// 访问方法
public int BugID
{
get
{
return bugID;
}
}
// 命名型参数的性质
public string Comment
{
get
{
return comment;
}
set
{
comment = value;
}
}
// 访问方法
public string Date
{get
{
return date;
}
}
// 访问方法
public string Programmer
{
get
{
return programmer;
}
}
// 私有成员数据
private int bugID;
private string comment;
private string date;
private string programmer;
}
// ********* 将属性信息赋值给类 ********
[BugFixAttribute(121,"Jesse Liberty","0I/O3/05")]
[BugFixAttribute(107,"Jesse Liberty","0I/O4/05",
Comment="Fixed off by one errors")]
public class MyMath
{
public double DoFunc1(double param1)
{
return param1 + DoFunc2(param1);
}
public double DoFunc2(double param1)
{
return param1 / 3;
}
}
public class Tester
{
public static void Main()
{
MyMath mm = new MyMath();
Console.WriteLine("Calling DoFunc(7). Result: {0}",
mm.DoFunc1(7));
}
}
}
输出:
Calling DoFunc(7). Result: 9.3333333333333339

10.反射
 
反射适用的范围:
· 浏览元数据信息
· 查看和实例化配件中的类型
· 后期绑定,在运行的时候创建类型的方法
· 运行时创建类型
下面逐个介绍:
浏览元数据
// Using GetType to obtain type information:
int i = 42;
System.Type type = i.GetType();
System.Console.WriteLine(type);
输出为:
System.Int32
浏览MyMath的元数据
System.Reflection.MemberInfo inf = typeof(MyMath);
对MyMath类型调用typeof操作符,返回一个Type类型的对象,它是从MemberInfo
派生而来的。
注意: Type类是反射类的根。它封装了对象类型的表示。Type 类是访问元数据的主要方式。Type 从MemberInfo 派生而来,封装了类成员(即方法、性质、字段、事件等)的相关信息。
下一步是调用此MemberInfo对象的GetCustomAttributes()方法,传入要查寻
的属性信息的类型。获得的结果是一个BugFixAttribute 类型的对象数组:
object[] attributes;
attributes =
inf.GetCustomAttributes(typeof(BugFixAttribute),false);
现在遍历此数组,输出BugFixAttribute对象的性质。例18-2 替换了例18-1 中的
Tester 类。
使用反射
public static void Main()
{
MyMath mm = new MyMath();
Console.WriteLine("Calling DoFunc(7). Result: {0}",
mm.DoFunc1(7));
// 获取成员信息并用它来
// 获取自定义属性信息
System.Reflection.MemberInfo inf = typeof(MyMath);
object[] attributes;
attributes =
inf.GetCustomAttributes(
typeof(BugFixAttribute), false);
// 遍历属性信息
// 获取性质
foreach(Object attribute in attributes)
{
BugFixAttribute bfa = (BugFixAttribute) attribute;
Console.WriteLine("/nBugID: {0}", bfa.BugID);
Console.WriteLine("Programmer: {0}", bfa.Programmer);
Console.WriteLine("Date: {0}", bfa.Date);
Console.WriteLine("Comment: {0}", bfa.Comment);
}
}
输出:
Calling DoFunc(7). Result: 9.3333333333333339
BugID: 121
Programmer: Jesse Liberty
Date: 0I/O3/05
Comment:
BugID: 107
Programmer: Jesse Liberty
Date: 0I/O4/05
Comment: Fixed off by one errors
当把这段代码替换到例18-1 中并运行时,可以看到元数据会按我们希望的那样输出。

查找类型
// Using Reflection to get information from an Assembly:
System.Reflection.Assembly o = System.Reflection.Assembly.Load("mscorlib.dll");
System.Console.WriteLine(o.GetName());
输出为:

mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

反射一个类型
也可以只反射mscorlib配件中的一个类型。为此,用GetType()方法从配件中提
取一个类型,如例18-4 所示。
例18-4:反射一个类型
namespace Programming_CSharp
{
using System;
using System.Reflection;
public class Tester
{
public static void Main()
{
// 查看对象
Type theType =
Type.GetType(
"System.Reflection.Assembly");
Console.WriteLine(
"/nSingle Type is {0}/n", theType);
}
}
}
输出:
Single Type is System.Reflection.Assembly


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值