在 dotnet 里面,可以使用 FormatterServices 的 GetUninitializedObject 方法可以实现只创建对象,而不调用对象的构造函数方法。而如果在使用此方法时,存在了 DLL 缺失的情况,此时能否让此方法运行通过,创建出空的对象

答案是可以创建成功,也可以创建不成功。当所有碰到的字段都是引用类型的时候,可以创建成功。如果存在值类型,但是值类型的 DLL 定义文件被删除,将会失败

下面来写一点测试的逻辑,如下面代码分别定义 F1 和 F2 和 F3 三个不同的类型

    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 对象还是被创建才出来,不会炸掉

上面代码放在 githubgitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 2f00793486fcb1962de7e368ec527cf1169db135

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git

获取代码之后,进入 JinaldalurhaBelnallbune 文件夹

其实此时即使获取 F2 的类型,通过反射拿到所有的成员,也是可以获取到的,如下图

可以看到原本是 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 所在的程序集的定义

上面代码放在 githubgitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 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 里面加载程序集的机制

更多请看 dotnet C# 只创建对象不调用构造函数方法

dotnet 使用 FormatterServices 的 GetUninitializedObject 方法在丢失 DLL 情况下能否执行的更多相关文章

  1. delphi中的ClientDataSet组件的open和Execute方法各自在什么情况下用?

    ClientDataSet组件本来是给midas用的,也是所谓的borland的三层数据技术,使用这个控件必须发行midas.dll挺麻烦的 open是通过应用的SQL语句为SELECTexecute ...

  2. Linux在丢失的情况下重置密码

    1.开机菜单是 移动光标到第一行 --敲击e 2.找到UTF-8,加上空格rd.break,敲击ctrl+x 3.输入以下命令 mount -o remount,rw /sysroot chroot ...

  3. DotNet生成随机数的一些方法

    在项目开发中,一般都会使用到“随机数”,但是在DotNet中的随机数并非真正的随机数,可在一些情况下生成重复的数字,现在总结一下在项目中生成随机数的方法. 1.随机布尔值: /// <summa ...

  4. ASP.NET Session丢失的情况

    正常操作情况下会有ASP.NET Session丢失的情况出现.因为程序是在不停的被操作,排除Session超时的可能.另外,Session超时时间被设定成60分钟,不会这么快就超时的. 现在我就把原 ...

  5. 在没备份undo的情况下,undo丢失,重启数据库报ORA-01157错误

    今天做了一下undo隐藏参数的实验 在没有备份的情况下,删除正在使用的undo,然后关机 (本次使用的的oracle的隐藏参数,慎用!!!!!!!!!!!!!!) idle> select * ...

  6. 后端把Long类型的数据传给前端,前端可能会出现精度丢失的情况,以及解决方案

    后端把Long类型的数据传给前端,前端可能会出现精度丢失的情况.例如:201511200001725439这样一个Long类型的整数,传给前端后会变成201511200001725440. 解决方法: ...

  7. RabbitMQ-如何保证消息在99.99%的情况下不丢失

    1. 简介 MQ虽然帮我们解决了很多问题,但是也带来了很多问题,其中最麻烦的就是,如何保证消息的可靠性传输. 我们在聊如何保证消息的可靠性传输之前,先考虑下哪些情况下会出现消息丢失的情况. 首先,上图 ...

  8. 什么情况下才要重写Objective-C中的description方法

    特别注意: 千万不要在description方法中同时使用%@和self,同时使用了%@和self,代表要调用self的description方法,因此最终会导致程序陷入死循环,循环调用descrip ...

  9. 如何让ASP.NET Web API的Action方法在希望的Culture下执行

    在今天编辑推荐的<Hello Web API系列教程--Web API与国际化>一文中,作者通过自定义的HttpMessageHandler的方式根据请求的Accep-Language报头 ...

  10. 关于在gridview中有dorpdownlist的情况下使用自带编辑模板的方法

    今天记录一下在gridview中,如果有dropdownlist的情况下使用gridview自带编辑模式的方法. 好吧,今天的这个问题有点绕,详细解释一下目的. 因为gridview中的某些列的数据是 ...

随机推荐

  1. .net core WPF关于进程打开网页链接报错"文件找不到"

    System.Diagnostics.Process proc = new System.Diagnostics.Process(); proc.StartInfo.FileName = " ...

  2. IDEA (任意 JetBrains IDE)拆分先前 commit

    最近在合并上游代码,遇到了一个问题:某个 commit 杂糅了几个不同的特性修改,这可能会导致 rebase 上游代码时需要再对该 commit 进行额外的代码冲突处理 解决方法:合并上游分支前,拆分 ...

  3. Windows上部署spring boot jar项目

    1.下载地址:https://github.com/winsw/winsw/releases 下载红色框内三个文件就够了. sample-allOptions.xml 所有配置参考 sample-mi ...

  4. 感悟:FPGA的并行处理与PC的多线程处理

    前言 FPGA的并行设计是其高速处理的核心之一, 通过并行地处理大量的数据实现预期的功能; PC的多线程设计则是处理大量的内容而衍生出的一种处理方式, 其本质是利用CPU的高速处理能力, 将单个线程以 ...

  5. C++代码实现OnComponentHit事件粒子消失蓝图--斯坦福

    蓝图节点 OnComponentBeginOverlap,OnComponentHit等等之类如何迁移到C++中 方法 这些蓝图节点实际上就是一个UE4已经定义好的事件,在蓝图中使用模块的连接来实现事 ...

  6. C++中结构体的各种用法,详细版

    C++中的结构体是一种用户自定义的数据类型,可以存储不同类型的数据成员.以下是一些结构体的常见用法: 1. 定义结构体类型 结构体类型的定义可以放在函数内部或外部,例如: ``` struct Per ...

  7. Scala mutable.Map可变的Map

    1 package chapter07 2 3 import scala.collection.mutable 4 5 object Test09_MutableMap { 6 def main(ar ...

  8. 【已解决】Hadoop未知的主机名master

  9. 强!10.6K star,一款开源HTTP测试工具,适合新手,简单、容易上手!

    大家好,我是狂师! 今天给大家推荐一款开源的HTTP测试工具:Hurl,相比curl.wget功能更强大,且更容易上手.很适用新手使用. 1.项目介绍 Hurl是一个使用Rust语言开发的命令行工具, ...

  10. #根号分治,前缀和,双指针#CF1446D2 Frequency Problem (Hard Version)

    题目 给定一个长度为 \(n\) 的序列,问是否存在一个最长的区间使得至少存在两个众数. 分析 实际上 Easy Version 是用来启发大于根号的做法的. 众数可以说有一个性质吧,答案区间中的其中 ...