大家都知道AssemblyVersionAttribute是用来指定Assembly的版本号使用的,但是不知道你有没有考虑过这个问题:这个Attribute真的生成到了最后的Assembly中吗?
我们建立一个简单的C#项目试一下便可以知道。在新建的C#项目中AssemblyInfo.cs缺省有如下的内容:
using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("TestAssemblyVersionAttribute")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("MSIT")] [assembly: AssemblyProduct("TestAssemblyVersionAttribute")] [assembly: AssemblyCopyright("Copyright © MSIT 2008")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("a39eea23-7bc8-47c2-aca8-93136279a0ec")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] |
Build一下,生成的文件用ILDASM查看,大部分Attribute都可以在Manifest中找到:
// Metadata version: v2.0.50727 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 2:0:0:0 } .assembly TestAssemblyVersionAttribute { .custom instance void [mscorlib]System.Reflection.AssemblyTitleAttribute::.ctor(string) = ( 01 00 1C 54 65 73 74 41 73 73 65 6D 62 6C 79 56 // ...TestAssemblyV 65 72 73 69 6F 6E 41 74 74 72 69 62 75 74 65 00 // ersionAttribute. 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyDescriptionAttribute::.ctor(string) = ( 01 00 00 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyConfigurationAttribute::.ctor(string) = ( 01 00 00 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyCompanyAttribute::.ctor(string) = ( 01 00 04 4D 53 49 54 00 00 ) // ...MSIT.. .custom instance void [mscorlib]System.Reflection.AssemblyProductAttribute::.ctor(string) = ( 01 00 1C 54 65 73 74 41 73 73 65 6D 62 6C 79 56 // ...TestAssemblyV 65 72 73 69 6F 6E 41 74 74 72 69 62 75 74 65 00 // ersionAttribute. 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyCopyrightAttribute::.ctor(string) = ( 01 00 16 43 6F 70 79 72 69 67 68 74 20 C2 A9 20 // ...Copyright .. 4D 53 49 54 20 32 30 30 38 00 00 ) // MSIT 2008.. .custom instance void [mscorlib]System.Reflection.AssemblyTrademarkAttribute::.ctor(string) = ( 01 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.InteropServices.ComVisibleAttribute::.ctor(bool) = ( 01 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.InteropServices.GuidAttribute::.ctor(string) = ( 01 00 24 61 33 39 65 65 61 32 33 2D 37 62 63 38 // ..$a39eea23-7bc8 2D 34 37 63 32 2D 61 63 61 38 2D 39 33 31 33 36 // -47c2-aca8-93136 32 37 39 61 30 65 63 00 00 ) // 279a0ec.. .custom instance void [mscorlib]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 31 2E 30 2E 30 2E 30 00 00 ) // ...1.0.0.0.. // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. .hash algorithm 0x00008004 .ver 1:0:0:0 } .module TestAssemblyVersionAttribute.exe // MVID: {383D6DF2-7A1D-41BA-944D-26979117014E} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // Image base: 0x00350000
|
只有一个例外,也就是AssemblyVersionAttribute在这个Manifest中是找不到的。而真正的指定Version的语句在这里:
.ver 1:0:0:0
|
我们再用另外一种方式,仿照C#编译器来动态生成一个Assembly来试一下:
AssemblyName name = new AssemblyName(); name.Name = "Result"; name.Version = new Version("1.1");
AssemblyBuilder asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.ReflectionOnly); ConstructorInfo asmVersionCtor = typeof(AssemblyVersionAttribute).GetConstructor(new Type[] { typeof(string) }); CustomAttributeBuilder attrBuilder = new CustomAttributeBuilder(asmVersionCtor, new object[] { "2.2" }); asmBuilder.SetCustomAttribute(attrBuilder);
asmBuilder.Save("Result.DLL");
|
这里我们指定Assembly版本号为1.1,而AssemblyVersionAttribute则是2.2,那么最终的结果如何呢?
// Metadata version: v2.0.50727 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 2:0:0:0 } .assembly Result { .custom instance void [mscorlib]System.Reflection.AssemblyVersionAttribute::.ctor(string) = ( 01 00 03 32 2E 32 00 00 ) // ...2.2.. .hash algorithm 0x00008004 .ver 1:1:0:0 } .module RefEmit_OnDiskManifestModule // MVID: {AF35088A-F419-4F88-A011-D29FF41C1E20} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000001 // ILONLY // Image base: 0x00880000
|
可以看到,首先AssemblyVersionAttribute并没有决定Assembly的版本号,而AssemblyVersionAttribute本身却被放到了Assembly的Manifest里面,反而和c#编译器的结果不一样了。
事实上,AssemblyVersionAttribute对于CLR本身并没有意义,并不能指定Assembly的版本号。AssemblyVersionAttribute本身只是对于编译器比如c#或者VB.NET等才有意义,编译器根据Attribute的内容生成对应版本号的文件,这个Attribute的内容本身并没有最后进入结果的Assembly。
除此之外,还有一些Attribute本身并不是CustomAttribute,而是作为Metadata中的特殊信息存在,可以是Bit Flag,也可以是Signature的一部分。典型的例子是和COM Interop有关的Attribute,如MarshalAsAttribute,DllImportAttribute,StructLayoutAttribute,比如:
[DllImport("kernel32.dll", EntryPoint="Beep", PreserveSig=true, SetLastError=true)] [return:MarshalAs(UnmanagedType.Bool)] public static extern bool MyBeep(uint freq, uint duration);
|
实际上对应的则是:
.method public hidebysig static pinvokeimpl("kernel32.dll" as "Beep" lasterr winapi) bool marshal( bool) MyBeep(uint32 freq, uint32 duration) cil managed preservesig { }
|
其中pinvokeimpl对应DllImportAttribute,”kernel32.dll” as “Beep”很明显对应DLL名字和EntryPoint,lasterr则是SetLastError=true,marshal(bool)对应MarshalAs(bool),preservesig对应PreserveSig=true。其中,有些信息是整个函数的Signature的一部分,有些则是BitFlag,CLR在运行的时候动态解析这些信息速度会更快,比CustomAttribute要快很多,原因很简单:我们随便找一个CustomAttribute来看:
.assembly Result { .custom instance void [mscorlib]System.Reflection.AssemblyVersionAttribute::.ctor(string) = ( 01 00 03 32 2E 32 00 00 ) // ...2.2.. .hash algorithm 0x00008004 .ver 1:1:0:0 }
|
看看上面的AssemblyVersionAttribute,内容为2.2,这个2.2是怎么读出来的呢?.ctor(string)表示构造Attribute的实例的时候要调用参数是String的构造函数,而后面的那一串数字,则是根据CLI所定义的规则指定的构造函数的参数:第一个01 00是16位长的标识,总是不变,03是字符串长度,后面的32 2E 32是字符串的UTF8编码,也就是2.2(这里只讲到字符串,对于其他不同类型的参数解释方式又不一样),00 00是16位长的0,表示后面跟0个可选命名参数(比如上面提到的PreserveSig=true这样的形式)。换句话说,CustomAttribute是需要CLR在运行时候动态调用构造函数,并分析这一串二进制数值得出参数来创建一个新的CustomAttribute的实例,比动态解析Metadata要慢。当然了,只有CLR自己支持的特殊Attribute才有这种待遇了,一般我们写的Attribute还是要老老实实的这么构造的。这些特殊的Attribute,用GetCustomAttributes函数是无法得到的,而只能用特殊方法获得。比如PreserveSig只能用GetMethodImplementationFlags函数获得。
Adam Nathan有一篇文章也讲到了这个问题,并有一个比较详尽的列表,有兴趣的朋友也可以参考一下他的这篇文章:http://blogs.msdn.com/adam_nathan/archive/2003/04/29/56645.aspx
作者: 张羿(ATField)
Blog: http://blog.csdn.net/atfield
http://blogs.msdn.com/yizhang
转载请注明出处