以下内容引用自:https://www.xuebuyuan.com/131146.html
众所周知,引用程序集的加载并不是在程序开始运行时就全部加载的,CLR会加载相应的程序集当该程序集的类型被第一次使用。更具体的说,当一个方法被JIT时,CLR会确保该方法中的类型所在的程序集被加载。
比如我们的主程序Mgen.exe引用一个类库ClassLibrary1,后者有一个类型Class1:
这段代码,分别在使用ClassLibrary1的一个类型之前和之后对当前应用程序域加载的程序集进行枚举:
static void Main() { foreach (var ass in AppDomain.CurrentDomain.GetAssemblies()) Console.WriteLine(ass.GetName().Name);
Console.WriteLine("=== 分割线 ==="); //使用ClassLibrary1的类型 doo(); foreach (var ass in AppDomain.CurrentDomain.GetAssemblies()) Console.WriteLine(ass.GetName().Name); } static void doo() { var obj = new ClassLibrary1.Class1(); }
运行代码(注意要在Release发布模式),程序会输出:
mscorlib Mgen ClassLibrary1 === 分割线 === mscorlib Mgen ClassLibrary1
为何ClassLibrary1出现在第一个列表中?事实上如果JIT不对doo进行内联处理的话,当Main方法被JIT后,CLR是不会觉察到Main方法会使用ClassLibrary1的,因此ClassLibrary1是不会出现在列表中的,但运行时刻JIT的优化使得doo被内联在Main方法中,这样整个代码相当于:
static void Main() { //省略 //内联的doo var obj = new ClassLibrary1.Class1(); //省略 }
使用MethodImplOptions.NoInlining可以阻止JIT在编译时把某些方法进行内联处理。注意MethodImpleOptions枚举要用在MethodImpl特性上。(另外MethodImpleOptions枚举还有其他功能,比如常见的MethodImpleOptions.Synchronized用来进行线程同步。可以参考MSDN:http://msdn.microsoft.com/zh-cn/library/system.runtime.compilerservices.methodimploptions.aspx)
最后注意上述类都在System.Runtime.CompilerServices命名空间内。
这样的话在doo方法上加入MethodImpl特性:
//+ System.Runtime.CompilerServices; [MethodImpl(MethodImplOptions.NoInlining)] static void doo() { var obj = new ClassLibrary1.Class1(); }
再次编译整个程序,输出:
mscorlib Mgen === 分割线 === mscorlib Mgen ClassLibrary1
可以看到,此时由于doo没有被内联,JIT编译Main方法后没有发现使用其他程序集,所以第一次枚举程序集没有ClassLibrary1,而当doo方法真正执行后,更确切的说是在doo方法被JIT后,但在执行前,CLR会加载ClassLibrary1。所以第二次枚举应用程序域中的程序集时,ClassLibrary1在列表中。
如果在Debug调试模式下运行程序,结果也是上面的输出(ClassLibrary1不在列表1),因为调试模式下,JIT方法内联优化是被禁止的。
====================================================================================================================
我们再来看下MethodImplOptions 枚举,它定义了如何实现某方法的详细信息。 此枚举有一个 FlagsAttribute 属性,允许其成员值按位组合。
命名空间:System.Runtime.CompilerServices
程序集:mscorlib(在 mscorlib.dll 中)
成员名称 说明
由 .NET Compact Framework 支持 ForwardRef 指定声明该方法,但其实现在其他地方提供。
由 .NET Compact Framework 支持 InternalCall 指定一个内部调用。内部调用是对在公共语言运行库本身内部实现的方法的调用。
由 .NET Compact Framework 支持 NoInlining 指定此方法不能内联。
由 .NET Compact Framework 支持 PreserveSig 指定此方法签名完全按声明的样子导出。
由 .NET Compact Framework 支持 Synchronized 指定同时只能由一个线程执行该方法。静态方法锁定类型,而实例方法锁定实例。
在任何实例函数中只能有一个线程执行,并且在任何类的静态函数中只能有一个线程执行。 由 .NET Compact Framework 支持 Unmanaged 指定此方法是以非托管代码实现的。
备注 与 MethodImplAttribute 一起使用。 使用按位“或”运算符指定多个 MethodImplOptions 值。
Note注意 实例或类型上的锁定(如同使用 Synchronized 标志一样)对于公共类型是不推荐使用的,其原因在于除了不是自己的代码的其他代码可对公共类型和实例采用锁定。这可能导致死锁或其他同步问题。