元数据 (CLI)

通用语言架构（CLI）中的元数据是指嵌入通用中间语言（CIL）代码中的某些数据结构，用于描述代码的高级结构。元数据描述程序集中定义的所有类和类成员，以及当前程序集将从另一个程序集中调用的类和类成员。方法的元数据包含该方法的完整描述，包括类（以及包含该类的程序集）、return type和所有方法参数。

CLI语言列表编译器将生成元数据并将其存储在包含CIL的程序集中。当运行时执行CIL时，它将检查以确保被调用方法的元数据与存储在调用方法中的元数据相同。这确保了只能使用正确数量的参数和正确的参数类型来调用方法。

Windows Runtime应用程序平台存在于Windows 8和Windows Phone 8中，它使用CLI元数据格式来描述以任何支持的编程语言编写的代码的组件接口。与通用语言运行库中使用的不同之处在于，程序集通常不包含任何CIL指令。

## 特性

开发人员可以通过“特性”（attribute）将元数据添加到代码中。有两种类型的特性，自定义特性和伪自定义特性，对开发人员来说，它们具有相同的句法。代码中的特性是给编译器生成元数据的消息。在CIL中，诸如继承修饰符、作用域修饰符之类的元数据，以及几乎所有不是操作码或流的东西，也被称为特性。

自定义特性是一个常规的类，它继承自`attribute`类。自定义特性可以用于任何方法、属性、类或整个程序集，语法为：`[“AttributeName”（可选“parameter”对，可选“name=value”对）]`，如下所示：

```csharp
[Custom]
[Custom(1)]
[Custom(1, Comment="yes")]
```

CLI广泛使用自定义特性。Windows Communication Framework使用特性来定义服务契约，ASP.NET使用这些特性将方法公开为Web服务，LINQ to SQL使用它们来定义类到基础关系架构的映射，Microsoft Visual Studio使用它们将对象的属性分组在一起，类开发人员通过应用`[category]`自定义特性来指示对象类的类别。自定义特性由应用程序代码而不是CLR来解释。当编译器看到自定义特性时，它将生成CLR无法识别的自定义元数据。开发人员必须提供代码来读取元数据并对其进行操作。例如，示例中显示的特性可以由代码处理：

```csharp
class CustomAttribute : Attribute
{
   private int paramNumber = 0;
   private string comment = "";

   public CustomAttribute() { }
   public CustomAttribute(int num) { paramNumber = num; }

   public String Comment
   {
      set { comment = value; }
   }
}
```

类的名称将映射到特性名称。Visual C#编译器会自动在任何特性名称的末尾添加字符串“`Attribute`”。因此，每个特性类名都应该以这个字符串结尾，但定义一个没有`attribute`-后缀的属性是合法的。将特性附加到项时，编译器将同时查找文字名称和末尾添加了`attribute`的名称；即如果要编写`[Custom]`，编译器将同时查找`Custom`和`CustomAttribute`。如果两者都存在，编译器将失败。如果您不想冒歧义的风险，该特性可以以“`@`”为前缀，因此写入`[@Custom]`将与`CustomAttribute`不匹配。使用特性调用类的构造函数。支持重载构造函数。名称-值对映射到特性，名称表示特性的名称，提供的值由特性设置。

有时，程序员所附加的属性会有歧义。请考虑以下代码：

```csharp
[Orange]
public int ExampleMethod(string input)
{
    //method body goes here
}
```

What has been marked as orange? Is it the `ExampleMethod`, its return value, or perhaps the entire assembly? In this case, the compiler will default, and treat the attribute as being affixed to the method. If this is not what was intended, or if the author wishes to clarify their code, an attribute target may be specified. Writing `[return: Orange]` will mark the return value as orange, `[assembly: Orange]` will mark the entire assembly. The valid targets are `assembly`, `field`, `event`, `method`, `module`, `param`, `property`, `return` and `type`. 什么被标记为橙色？是`ExampleMethod`、它的返回值，还是整个程序集？在这种情况下，编译器将默认，并将特性视为附加到方法上。如果这不是预期的，或者如果作者希望澄清他们的代码，可以指定“特性目标”。写入`[return：Orange]`将返回值标记为橙色，`[assembly:Orange]`将标记整个程序集。有效的目标是`assembly`、`field`和`event`，`method`, `module`, `param`, `property`, `return` 和 `type`。

伪自定义特性与常规自定义特性一样使用，但它们没有自定义处理程序；相反，编译器具有对特性的内在感知，并以不同的方式处理标记有这些特性的代码。诸如`Serializable`和`Obsolete`之类的特性被实现为伪自定义特性。ILAsm不应该使用伪自定义特性，因为它有足够的语法来描述元数据。

## 元数据存储

程序集包含元数据表。这些表格由CIL规范描述。元数据表将具有零个或多个条目，并且条目的位置决定其索引。当CIL代码使用元数据时，它通过元数据令牌（token）来使用元数据。这是一个32-比特值，其中前8位标识适当的元数据表，其余24位给出表中元数据的索引。Framework SDK包含一个名为metainfo的示例，该示例将列出程序集中的元数据表，但是，这些信息很少对开发人员有用。可以使用.NET Framework SDK提供的ILDASM工具查看程序集中的元数据。

在CIL标准中，元数据以ILAsm（汇编语言）形式、用于存储的磁盘上表示形式以及嵌入可移植可执行文件（PE、.exe或.dll）格式的程序集中的形式进行定义。PE表格基于磁盘上的表格。

## 反射

反射是用于读取CLI元数据的API。反射API提供元数据的逻辑视图，而不是metainfo等工具提供的文本视图。.NET框架1.1版中的反射可用于检查类及其成员的描述，并调用方法。但是，它不允许在运行时访问方法的CIL。该框架的2.0版允许获得方法的CIL。

## 其他元数据工具

除了SystemReflection命名空间之外，还提供了其他可用于处理元数据的工具。Microsoft.NET Framework附带了一个CLR元数据操作库，该库在原生代码中实现。检索和操作元数据的第三方工具包括和也可以使用。

## 基本资料

- 日语原文：CLI元数据, 磁盘上表示
- 标准：ECMA-335 part II
- magic：0x424A5342
- 开发者：Microsoft、Ecma International
- 扩展名：.exe, .dll, .winmd