C反射与特性:深入探索元数据操作
立即解锁
发布时间: 2025-08-21 00:06:09 阅读量: 2 订阅数: 8 


C#编程基础与实战技巧
### C# 反射与特性:深入探索元数据操作
#### 1. 元数据与反射基础
在编程世界里,大部分程序都是围绕数据展开工作的,它们负责读取、写入、操作和展示数据,像图形其实也是数据的一种形式。程序员所创建和使用的类型,都是为了这些数据处理目的而设计的,在设计阶段,程序员就需要了解所使用类型的特性。
不过,有一些程序处理的数据并非是数字、文本或者图形,而是关于程序及其类型的信息,这些信息被称为元数据,它们存储在程序的程序集中。当一个正在运行的程序查看自身或者其他程序的元数据时,这一过程就被叫做反射。比如对象浏览器就是一个能显示元数据的程序,它可以读取程序集并展示其中包含的类型以及所有特性和成员。
需要注意的是,若要使用反射,必须引入 `System.Reflection` 命名空间。
#### 2. Type 类详解
在 C# 中,有各种各样的类型,包括预定义类型(如 `int`、`long`、`string` 等)、基础类库(BCL)中的类型(如 `Console`、`IEnumerable` 等)以及用户自定义类型(如 `MyClass`、`MyDel` 等),每个类型都有自己的成员和特性。
BCL 中声明了一个抽象类 `Type`,它的作用是包含类型的特性。借助这个类的对象,我们可以获取程序所使用类型的相关信息。由于 `Type` 是抽象类,无法直接创建实例,在运行时,公共语言运行时(CLR)会创建 `Type` 的派生类 `RuntimeType` 的实例,这些实例包含了类型信息。当我们访问这些实例时,CLR 返回的是基类 `Type` 的引用。为了方便,后续统一将这个引用指向的对象称为 `Type` 类型的对象。
关于 `Type` 类,有几个重要的点需要知道:
- 对于程序中使用的每个类型,CLR 都会创建一个包含该类型信息的 `Type` 对象。
- 程序中使用的每个类型都与一个单独的 `Type` 对象相关联。
- 无论一个类型创建了多少个实例,所有实例都只关联一个 `Type` 对象。
下面是 `System.Type` 类的一些常用成员列表:
| 成员 | 成员类型 | 描述 |
| ---- | ---- | ---- |
| Name | 属性 | 返回类型的名称。 |
| Namespace | 属性 | 返回包含类型声明的命名空间。 |
| Assembly | 属性 | 返回类型声明所在的程序集。如果是泛型类型,则返回类型定义所在的程序集。 |
| GetFields | 方法 | 返回类型的字段列表。 |
| GetProperties | 方法 | 返回类型的属性列表。 |
| GetMethods | 方法 | 返回类型的方法列表。 |
#### 3. 获取 Type 对象的方法
获取 `Type` 对象有多种方式,这里主要介绍使用 `GetType` 方法和 `typeof` 运算符。
##### 3.1 使用 GetType 方法
`Type` 对象有一个 `GetType` 方法,它会返回一个指向实例 `Type` 对象的引用。因为所有类型最终都派生自 `object`,所以可以对任何类型的对象调用 `GetType` 方法来获取其 `Type` 对象,示例代码如下:
```csharp
Type t = myInstance.GetType();
```
下面是一个完整的示例代码,展示了如何使用 `GetType` 方法获取类型信息:
```csharp
using System;
using System.Reflection;
class BaseClass
{
public int BaseField = 0;
}
class DerivedClass : BaseClass
{
public int DerivedField = 0;
}
class Program
{
static void Main()
{
var bc = new BaseClass();
var dc = new DerivedClass();
BaseClass[] bca = new BaseClass[] { bc, dc };
foreach (var v in bca)
{
Type t = v.GetType();
Console.WriteLine("Object type : {0}", t.Name);
FieldInfo[] fi = t.GetFields();
foreach (var f in fi)
Console.WriteLine(" Field : {0}", f.Name);
Console.WriteLine();
}
}
}
```
上述代码的输出结果如下:
```
Object type : BaseClass
Field : BaseField
Object type : DerivedClass
Field : DerivedField
Field : BaseField
```
##### 3.2 使用 typeof 运算符
使用 `typeof` 运算符也能获取 `Type` 对象,只需将类型名称作为操作数,它就会返回指向该类型 `Type` 对象的引用,示例如下:
```csharp
Type t = typeof(DerivedClass);
```
下面是一个使用 `typeof` 运算符的简单示例代码:
```csharp
using System;
using System.Reflection;
namespace SimpleReflection
{
class BaseClass
{
public int MyFieldBase;
}
class DerivedClass : BaseClass
{
public int MyFieldDerived;
}
class Program
{
static void Main()
{
Type tbc = typeof(DerivedClass);
Console.WriteLine("Result is {0}.", tbc.Name);
Console.WriteLine("It has the following fields:");
FieldInfo[] fi = tbc.GetFields();
foreach (var f in fi)
Console.WriteLine(" {0}", f.Name);
}
}
}
```
该代码的输出结果为:
```
Result is DerivedClass.
It has the following fields:
MyFieldDerived
MyFieldBase
```
#### 4. 特性的概念与应用
特性是一种语言结构,它允许我们向程序的程序集添加元数据,是一种用于存储程序构造信息的特殊类。
- **目标**:应用特性的程序构造被称为目标。
- **消费者**:像对象浏览器这类用于检索和使用元数据的程序,被称为特性的消费者。
- **特性类型**:.NET 中既有预定义的特性,也可以自定义特性。
特性的命名通常采用帕斯卡命名法,并且以 `Attribute` 后缀结尾。在将特性应用到目标时,可以省略这个后缀。例如对于 `SerializableAttribute` 和 `MyAttributeAttribute`,在应用到构造时可以使用 `Serializable` 和 `MyAttribute` 这样的短名称。
应用特性时,需要在构造之前放置一个特性部分。特性部分由方括号括起来,里面包含特性名称,有时还会有参数列表。例如:
```csharp
[ Serializable ]
public class MyClass
{
...
}
[ MyAttribute("Simple class", "Version 3.57") ]
public class MyOtherClass
{
...
}
```
关于特性,还有几个重要的点需要了解:
- 大多数特性只应用于特性部分之后紧接着的构造。
- 应用了特性的构造被称为用特性修饰或装饰,这两种说法都很常见。
#### 5. 预定义保留特性
在了解自定义特性之前,先看看 .NET 预定义和保留的两种特性:`Obsolete` 和 `Conditional`。
##### 5.1 Obsolete 特性
`Obsolete` 特性可以将一个程序构造标记为过时,并在编译代码时显示一条有用的警告消息。示例代码如下:
```csharp
class Program
{
[Obsolete("Use method SuperPrintOut")]
static void Prin
```
0
0
复制全文