【IT168 技术】大家都知道,在.NET里面,一旦加载了一个程序集,就永远没有办法卸载,除非你将整个AppDomain给卸了。这可真是一个远古的问题啊,想当年为此我们真是感觉非常的不爽。因为我们不得不为了一些场景而创建AppDomain,然后再这个Domain里面加载,然后做跨域调用,最后再卸载这个AppDomain。
但是到了.NET 4.0里面,一切就不一样了:在特定情况下,一个Assembly是可以被卸载的。是否很兴奋呢?嗯,别高兴太早,这可是有很多条件的。
条件
1、这个程序集必须是动态创建的,并且使用特殊参数;
2、这个可以动态卸载的程序集,里面所有的类,都不允许被静态引用(注意,不是指被静态程序集引用,而是指被派生等作为编码的一部分,参见二楼回复);
3、你不能明确要求卸载哪一个程序集,你只能要求GC回收所有对象(也就包括了不再使用的程序集);
4、这个动态程序集不可以被保存到磁盘上(这真是一个诡异的条件)。
解释
1、啥都不说了,直接上示例:
assemblyName,
AssemblyBuilderAccess.RunAndCollect
);
也就是说,你必须使用这个新的枚举值RunAndCollect,那么这个程序集才能够被动态收集。反过来说,如果你想要有
2、这个需要稍微解释一下,那就是你在这个动态程序集A中创建出来的类,是不能被一个不可回收的(动态)程序集B中的类所继承的,因为这样就导致该程序集不可能被回收了。至于说能否作为B程序集中某个变量或者字段的类型,那这个我还真没有仔细考究,按照同样的逻辑,应该也是不行的。(这一条我还真没有认真考究,因为我的应用场景里面根本就没有这种可能性。)
3、可回收动态程序集的这个程序集本身,会被看作是一个普通的对象,如果这个对象中的任意部分存在引用计数,那么GC就不会回收。为啥不能有一个Unload方法来卸载呢?我想应该是如果存在这种方法的话,那就会大大增加了错误回收导致出现运行时错误的问题。
4、这一条嘛,很明显RunAndCollect和RunAndSave是两个不同的枚举值,不能同时给出。至于原因,还真是觉得比较困惑。在我做测试的时候,还非得把这个参数改成RunAndSave然后才能保存,以便用Reflector看有什么地方写错了。为啥不让存呢?存了在加载就当作静态的好了。这个我也没有考究,因为我除了在调试的时候有可能需要保存下来看看之外,没有这种需要。
嗯,我承认,此文质量有所欠缺。那么,原谅我吧,我待会儿就要上火车了,所以没时间好好写写。不过呢,我还真搜了一下,似乎这是第一篇提到这个4.0新特性的中文帖子(至少在cnblogs里面是这样吧)。所以呢,我想在这里抛砖引玉,让大家找找4.0里面一些犄角旮旯里面的新东西,假如你有兴趣,甚至有应用需要的话。
这里也给出一个EntityFramework的小题目,如果有兴趣,这些题目的实验结果也是比较有趣的。
假如有一个User表,包含以下字段:
ID : int
Name : nvarchar
Description: nvarchar
表中数据如下:
2 B YYY
3 C ZZZ
那么,我们用EntityFramework构造好模型之后,能否做以下的查询:
select new object[] { item.Name, item.Description };
如果用下面这个做查询,会得到什么样的结果呢?(你会看到一个很有意思的Bug哦!)
select new List(){ item.Name, item.Description };
如果用下面这个做查询,又会有什么样的结果呢?
select new List(){ item.Name, item.Id };
为了抢占这个Bug的发现权,我先公布一下第二个查询的实际结果:
{
Console.WriteLine(item[0] + "," + item[1]);
}
输出:
A,XXX
YYY,B
C,ZZZ
为什么我会想到这些问题呢?这些问题又怎么会跟动态程序集的可回收联系在一起了呢?这是因为我需要做的一些很有趣的东西。
那么,这个有趣的东西又是什么呢?下一期的节目,更加精彩。