在 dotnet 里面,可使用 FormatterServices 的 GetUninitializedObject 方法能够实现只建立对象,而不调用对象的构造函数方法。而若是在使用此方法时,存在了 DLL 缺失的状况,此时可否让此方法运行经过,建立出空的对象html
答案是能够建立成功,也能够建立不成功。当全部碰到的字段都是引用类型的时候,能够建立成功。若是存在值类型,可是值类型的 DLL 定义文件被删除,将会失败git
下面来写一点测试的逻辑,以下面代码分别定义 F1 和 F2 和 F3 三个不一样的类型github
class F1 { public F2 F2 { get; } = new F2(); } class F2 { public F3 F3 { get; } = new F3(); } public class F3 { }
在 Main 函数里面使用下面代码调用 FormatterServices 的 GetUninitializedObject 方法建立对象函数
class Program { static void Main(string[] args) { var f1 = FormatterServices.GetUninitializedObject(typeof(F1)); } }
接着将 F3 类放在另外一个项目里面,而后让此项目引用包含 F3 类的项目。在构建完成以后,删除包含 F3 类的项目的输出 DLL 文件。接着运行 Main 方法,能够看到实际上 f1 对象仍是被建立才出来,不会炸掉post
能够经过以下方式获取本文的源代码,先建立一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入如下代码,便可获取到本文的代码命令行
git init git remote add origin https://gitee.com/lindexi/lindexi_gd.git git pull origin 2f00793486fcb1962de7e368ec527cf1169db135
以上使用的是 gitee 的源,若是 gitee 不能访问,请替换为 github 的源code
git remote remove origin git remote add origin https://github.com/lindexi/lindexi_gd.git
获取代码以后,进入 JinaldalurhaBelnallbune 文件夹orm
其实此时即便获取 F2 的类型,经过反射拿到全部的成员,也是能够获取到的,以下图htm
能够看到本来是 F3 的类型对应的属性,在反射拿到的是 System.Reflection.RuntimePropertyInfo
类型
能够看到对应的模块被删除时,只会提示说文件找不到,而不会让反射失败
接下来试试使用结构体的方式,也就是字段实际是值类型的方式,修改 F2 和 F3 从引用类型修改成结构体,代码以下
struct F2 { public F3 F3 { get; } } public struct F3 { static F3() { } }
依然将 F3 放在另外一个程序集,而后在输出文件里面删除此程序集的 DLL 文件。尝试运行代码,能够看到此时运行将会失败
缘由是由于值类型须要计算对象的占用的内存空间的大小,在准备建立 F1 的时候须要开始计算 F2 的占用空间,由于 F2 是一个结构体。可是 F2 里面引用了 F3 类型,此时 F2 就须要开始计算 F3 的空间,然而定义 F3 占用空间大小的数据放在了被删除的程序集里面,所以拿不到 F3 的占用空间大小,从而计算不出 F2 的空间大小,也就没法建立 F1 对象,所以失败
那为何 F3 的占用空间大小须要放在定义 F3 的程序集里面,不能放在被引用的如 F2 所在的程序集里面?缘由在于 dotnet 的应用能够支持 DLL 兼容更新,如我能够方便的更改 F3 类型的定义,如添加一个字段。那么此时 F3 的占用内存空间大小天然就须要修改了。然而此时我能够作到不更改 F2 所在的程序集,只须要更新 F3 所在的程序集便可,这就是由于在运行时里面读取了 F3 所在的程序集拿到了 F3 的占用内存空间的大小,不须要依赖在 F2 所在的程序集的定义
能够经过以下方式获取本文的源代码,先建立一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入如下代码,便可获取到本文的代码
git init git remote add origin https://gitee.com/lindexi/lindexi_gd.git git pull origin 415664a5516c778db662dd519e9114a320a4d690
以上使用的是 gitee 的源,若是 gitee 不能访问,请替换为 github 的源
git remote remove origin git remote add origin https://github.com/lindexi/lindexi_gd.git
获取代码以后,进入 JinaldalurhaBelnallbune 文件夹
若是不是直接的引用的类型找不到定义的程序集,那依然能够成功,将 F2 从结构体修改成引用类型,以下面代码
class F2 { public F3 F3 { get; } }
此时删除 F3 所在的程序集,依然能够建立出来 F1 对象
经过上文能够了解到 F1 对象的内存空间,能够计算出来,由于 F2 是引用类型,引用类型占用的字段内存空间是固定的。因此就不须要再去计算 F2 里面包含的 F3 结构体的占用空间
固然,依然让 F2 是结构体类型,可是将 F3 修改成引用类型,也能建立成功。缘由是 F2 结构体在不知道 F3 的程序集时依然能够根据引用类型占用的字段空间是固定的,计算出包含 F3 的属性的字段占用的内存,所以不须要去读取 F3 所在的程序集
经过上文能够了解到 dotnet 里面加载程序集的机制