原文在这里
概念: 要说MetadataToken,首先的先说说Metadata,MSDN对Metadata的定义是这样的“Metadata refers to declarative information about abstractions, including runtime types (classes, value types, and interfaces), global functions, and global variables.” (这里就不做翻译了,怕翻译不好造成误解)而对于这些Runtime的类型信息存贮在Metadata的一张张表中,我们知道这些表有定义表、引用表、指针表、堆。那么MetadataToken是什么呢?MetadataToken其实就是这些存贮在Metadata一张张表中的位置信息,它指向的就是这些表中的一个记录。对于Metadata Engine 来说,可以用这些Token去定位具体的一个Runtime Type。
深入:MetadataToken的结构就是一个4字节的int型整数,第一个字节指出这个MetadataToken指向的RuntimeType存贮在具体的哪一个Metadata表。 对于每个Metadata表的索引值由CorTokenType这个Enum来决定,CorTokenType的定义如下:
mdtModule = 0x00000000 ,
mdtTypeRef = 0x01000000 ,
mdtTypeDef = 0x02000000 ,
mdtFieldDef = 0x04000000 ,
mdtMethodDef = 0x06000000 ,
mdtParamDef = 0x08000000 ,
mdtInterfaceImpl = 0x09000000 ,
mdtMemberRef = 0x0a000000 ,
mdtCustomAttribute = 0x0c000000 ,
mdtPermission = 0x0e000000 ,
mdtSignature = 0x11000000 ,
mdtEvent = 0x14000000 ,
mdtProperty = 0x17000000 ,
mdtModuleRef = 0x1a000000 ,
mdtTypeSpec = 0x1b000000 ,
mdtAssembly = 0x20000000 ,
mdtAssemblyRef = 0x23000000 ,
mdtFile = 0x26000000 ,
mdtExportedType = 0x27000000 ,
mdtManifestResource = 0x28000000 ,
mdtGenericParam = 0x2a000000 ,
mdtMethodSpec = 0x2b000000 ,
mdtGenericParamConstraint = 0x2c000000 ,
mdtString = 0x70000000 ,
mdtName = 0x71000000 ,
mdtBaseType = 0x72000000
} CorTokenType;
具体每一个enum值代表什么我就不用说了,相比聪明的你一看就明白了。从CorTokenType的定义,机灵的读者会发现其实每个表里只能有16777216条记录。就mdtTypeDef来说,我们可不可以说在一个Module中我们最多只能定义16777216个type呢?!扯的有些远了,我们回到刚才的对MetadataToken结构的讨论。4字节的MetadataToken的后三个字节在其实就是一个RuntimeType在具体一个Metadata表中的位置,或者说是偏移量吧。 口说无凭,还是我们来点实际的吧!
例子 : 我们建一个简单的Console工程就可以了,定义一下三个type:
namespace ConsoleApplication3
{
interface IClass1
{
void Fun1();
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication3
{
class Class1 : ConsoleApplication3.IClass1
{
public void Fun1()
{
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;
namespace ConsoleApplication3
{
class Program
{
static void Main( string [] args)
{
Console.WriteLine( " typeof(Class1).MetadataToken = {0} " , Convert.ToString(
typeof (Class1).MetadataToken, 16 )
);
Console.WriteLine( " typeof(IClass1).MetadataToken = {0} " , Convert.ToString(
typeof (IClass1).MetadataToken, 16 )
);
Console.WriteLine( " typeof(Program).MetadataToken = {0} " , Convert.ToString(
typeof (Program).MetadataToken, 16 )
);
Console.Read();
}
}
}
我们先不要急着去Run它,我们可以用前面说的这些知识来分析,然后猜想一下结果。因为这三个都是类型,所以他们的MetadataToken的第一个字节肯定是0x020000, 因为这个项目里面只有三个类型,所以我想他们的MetadataToken肯定是0x0200002、0x02000003, 0x02000004(有一个很重要的忘说了,在每张Metadata表中的0位置是不包含数据的,MSDN说成为nil token, 0x02000001这个位置预留给了一个名为<Module>的特殊类型。程序中所有的全局字段和全局方法,其实都是这个<Module>类型的成员(这里多谢 Anders Liu 帮忙指正,谢谢)),现在我们可以看看结果了啊
和我们预计结果一致。其他的我就不举例了,有兴趣的朋友可以自己试试,这里要注意的是一点就是String类型的MetadataToken的后三个字节的代表的是这个string在Metadata string pool中的起始位置。
应用: 讲了这么多了,到底这个东东有什么用呢?该怎么样用呢?对于一个已经build的程序集,它其中的Metadata已经固定了,也就是说它的MetadataToken是固定。这样的话,有的时候我们就可以直接通过这个MetadataToken去获得它的RuntimeType,这点在泛型和反射有的时候是非常有用的。具体的使用如下:
ModuleHandle currentModuleHandle = currentModule.ModuleHandle;
int token = typeof (Program).MetadataToken;
RuntimeTypeHandle rth = currentModuleHandle.GetRuntimeTypeHandleFromMetadataToken(token);
Type currentType = Type.GetTypeFromHandle(rth);
就写到这了,其实Metadata还是很值得仔细学习一下,我现在了解的还是很肤浅,这里也只是抛砖引玉,希望得到高手的拍砖, 哈哈哈。。。。