深入.NET程序集加载:你知道.NET程序集加载的确切时机吗,来看看一段代码存在的问题

简介:

这个问题是一个有关.NET公共语言运行时的问题。大家知道,.NET为了提高程序性能,所有程序集是按需加载的,也就是说在必要时在加载。但是,你知道程序集确切的加载时机吗?绝大多数情况下,我们都不需要关心程序集的加载时机,不过,当我们在做程序自动升级的时候就碰到一些非常有趣的事情了。下面我先抛出问题。

 

首先,我们先来看看以下这段有问题的程序,你觉得哪有问题。我先来解释一下这段程序的用处:

(1)在WPF的App这类的构造函数中检测内核文件更新并启动OSGi.NET内核;

(2)UpdateCoreAndStartBundleRuntime的作用就是:A)更新UIShell.iOpenWorks.BundleRepositoryOpenAPI、UIShell.PageFlowService、UIShell.OSGi等程序集;B)在内核程序集更新完成后,创建一个BundleRuntime(表示OSGi.NET插件内核,来自UIShell.OSGi程序集)。

复制代码
namespace UIShell.iOpenWorks.WPF
{
    /// <summary>
    /// WPF startup class.
    /// </summary>
    public partial class App : Application
    {
        private BundleRuntime _bundleRuntime;

        public App()
        {
            UpdateCoreAndStartBundleRuntime()
        }

        void UpdateCoreAndStartBundleRuntime() 
        {
            // Update Core Files, including BundleRepositoryOpenAPI, PageFlowService and OSGi Core assemblies.
            if (AutoUpdateCoreFiles)
            {
                new CoreFileUpdater().UpdateCoreFiles(CoreFileUpdateCheckType.Daily);
            }

            // Start OSGi Core.
            var bundleRuntime = new BundleRuntime();
            bundleRuntime.AddService<Application>(this);
            bundleRuntime.Start();

            Startup += App_Startup;
            Exit += App_Exit;
            _bundleRuntime = bundleRuntime;
        }
     // Other codes...
    }
}
复制代码

上面这段代码很简单,但是是错误的,下面我们稍作细微调整,代码就能够正常工作了。

复制代码
namespace UIShell.iOpenWorks.WPF
{
    /// <summary>
    /// WPF startup class.
    /// </summary>
    public partial class App : Application
    {
        // Use object type to avoid loading UIShell.OSGi.dll before update.
        private object _bundleRuntime;

        public App()
        {
            UpdateCore();
            StartBundleRuntime();
        }

        void UpdateCore() // Update Core Files, including BundleRepositoryOpenAPI, PageFlowService and OSGi Core assemblies.
        {
            if (AutoUpdateCoreFiles)
            {
                new CoreFileUpdater().UpdateCoreFiles(CoreFileUpdateCheckType.Daily);
            }
        }

        void StartBundleRuntime() // Start OSGi Core.
        {
            var bundleRuntime = new BundleRuntime();
            bundleRuntime.AddService<Application>(this);
            bundleRuntime.Start();

            Startup += App_Startup;
            Exit += App_Exit;
            _bundleRuntime = bundleRuntime;
        }
        // Other codes ......
    }
}
复制代码

正确的代码和错误代码的差异有二:

(1)_bundleRuntime的类型由BundleRuntime变为object;

(2)将UpdateCoreAndStartBundleRuntime函数拆分成两个函数,分别为UpdateCore和StartBundleRuntime。

 

这两段代码的差别非常细微,从功能的角度来看,没有任何的差异。各位聪明的看官,为什么第一段代码是错误的呢?这就是.NET程序集晚加载所引起的。那我们先来看看,以下这段代码,BundleRuntime这个类型被使用并创建了一个实例,那么,这个方法一旦执行完成,UIShell.OSGi(BundleRuntime类型所在程序集)这个程序集肯定被加载了。但是,确切加载时机是什么时候?是在调用“var bundleRuntime = new BundleRuntime()”这行代码时,UIShell.OSGi所在程序集才加载吗?正确的答案是:UIShell.OSGi程序集加载时机是CLR进入UpdateCoreAndStartBuntime这个方法入口时,CLR会将这个方法所用到的类型的所在程序集全部加载进来。这样问题就来了,也就是在执行UIShell.OSGi这个程序集更新之前,UpdateCoreAndStartBuntime方法刚刚调用时,UIShell.OSGi程序集就会被加载,这是,你执行对UIShell.OSGi程序集更新的时候,就会碰到异常,提示这个文件已经被占用了。

 

复制代码
void UpdateCoreAndStartBundleRuntime() // Update Core Files, including BundleRepositoryOpenAPI, PageFlowService and OSGi Core assemblies.
        {
            if (AutoUpdateCoreFiles)
            {
                new CoreFileUpdater().UpdateCoreFiles(CoreFileUpdateCheckType.Daily);
            }

            var bundleRuntime = new BundleRuntime();
            bundleRuntime.AddService<Application>(this);
            bundleRuntime.Start();

            Startup += App_Startup;
            Exit += App_Exit;
            _bundleRuntime = bundleRuntime;
        }
复制代码

我们可以在该代码加上一个测试代码,然后看看UIShell.OSGi是否是刚进入方法之时,执行更新之前就加载了。答案是肯定的。这段代码很好说明了,BundleRuntime这个类型所在程序集UIShell.OSGi是在声明这个类型的方法调用时就加载。

image

以上问题的解决方法非常简单,就是将上面两段不同功能代码拆分到不同的函数。此时,我们可以再来调试一下代码。

image

你可以发现拆分后,这个问题就可以解决了。好了,大家应该明白关于程序集加载的确切时机了。不过,如果大家还想追究为什么程序集加载时机是在进入方法时,你还可以深入研究《.NET本质论》这本书,我建议大家看该书英文版的,对于深入理解CLR有非常大的帮助。


本文转自道法自然博客园博客,原文链接:http://www.cnblogs.com/baihmpgy/archive/2013/05/08/3066352.html,如需转载请自行联系原作者

目录
相关文章
|
9月前
.Net6新版本的AssemblyLoadContext 加载程序集和卸载程序集
.Net6新版本的AssemblyLoadContext 加载程序集和卸载程序集
95 0
.Net6新版本的AssemblyLoadContext 加载程序集和卸载程序集
|
10月前
|
编译器
.Net命名空间和程序集
我们都知道using有三个作用:·引入命名空间,创建别名,强制资源清理。这次对using 的“引入命名空间”的作用有了一点小疑问:命名空间和程序集有什么关系?
|
监控 .NET
重温.NET下Assembly的加载过程
原文:重温.NET下Assembly的加载过程 最近在工作中牵涉到了.NET下的一个古老的问题:Assembly的加载过程。虽然网上有很多文章介绍这部分内容,很多文章也是很久以前就已经出现了,但阅读之后发现,并没能解决我的问题,有些点写的不是特别详细,让人看完之后感觉还是云里雾里。
1100 0
C#.Net 如何动态加载与卸载程序集(.dll或者.exe)6-----在不卸载程序域的前提下替换程序集文件。
原文:C#.Net 如何动态加载与卸载程序集(.dll或者.exe)6-----在不卸载程序域的前提下替换程序集文件。 当某个程序集文件被载入AppDomain,该文件在AppDomain.Unload之前是不能被替换和删除的。
2296 0
|
存储 定位技术
ArcGIS Runtime for .Net Quartz开发探秘(四):加载本地文件
TilePackage(.tpk) TilePackage是使用地图文档(mxd)制作的用于移动端底图的地图包,本质压缩的切片文件和地图文档。 ArcGISTiledLayer tpkfile = new ArcGISTiledLayer(new Uri(FilePath)); myMapView.Map.OperationalLayers.Add(tpkfile); VectorTilePackage(.vtpk) Vtpk是使用ArcGIS Pro生成的矢量切片数据包,矢量切片包的介绍——矢量切片,集美貌与才华于一身的底图方案。
1557 0