.NET中几个怪异的CustomAttribute

 

大家都知道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.[Page]

                                                                                              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 .. [Page]

                                                                                                  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..[Page]

  .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/");[Page]

 

            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,比如:[Page]

 

        [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函数获得。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值