当前位置: 首页 > news >正文

(C#)if (this == null)?你在逗我,this 怎么可能为 null!用 IL 编译和反编译看穿一切

if (this == null) Console.WriteLine("this is null"); 这句话一写,大家一定觉得荒谬,然而 if 内代码的执行却是可能的!本文讲介绍到底发生了什么。


制造一个 this 可以为 null 的程序

请看代码,这是我们的库函数:

namespace Walterlv.Demo
{
    public class Foo
    {
        public void Test()
        {
            if (this == null) Console.WriteLine("this is null");
            else Console.WriteLine("this is not null");
        }
    }
}

外面是这样调用的:

namespace Walterlv.Demo
{
    public class Program
    {
        private static void Main()
        {
            Foo p = null;
            p.Test();
        }
    }
}

这代码写出来,当然毫不犹豫地说——这会发生 NullReferenceException

然而……

现在我们改一改 Program 的 IL:

Foo.Test

将关注重点放在图中红框标注的部分,那是调用 p.Test 的地方。

现在,我们将它从 callvirt 修改成 call

第一步:反编译 exe 成 IL:


# ildasm 在 C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.7.1 Tools\x64 路径下

ildasm /out=D:\Desktop\wdemo.il D:\Desktop\Walterlv.Demo\wdemo\bin\Debug\wdemo.exe

第二步:修改 IL,将 callvirt 修改成 call

IL_0004:  call   instance void Walterlv.Demo.Foo::Test()

第三步:重新编译 IL 成 exe


# ilasm 在 C:\Windows\Microsoft.NET\Framework64\v4.0.30319 路径下

lvyi> ilasm /out:D:\Desktop\wdemo.exe D:\Desktop\wdemo.il

Microsoft (R) .NET Framework IL Assembler.  Version 4.7.2556.0
Copyright (c) Microsoft Corporation.  All rights reserved.
Assembling 'D:\Desktop\wdemo.il'  to EXE --> 'D:\Desktop\wdemo.exe'
Source file is ANSI

Assembled method Walterlv.Demo.Program::Main
Assembled method Walterlv.Demo.Program::.ctor
Assembled method Walterlv.Demo.Foo::Test
Assembled method Walterlv.Demo.Foo::.ctor
Creating PE file

Emitting classes:
Class 1:        Walterlv.Demo.Program
Class 2:        Walterlv.Demo.Foo

Emitting fields and methods:
Global
Class 1 Methods: 2;
Class 2 Methods: 2;
Resolving local member refs: 1 -> 1 defs, 0 refs, 0 unresolved

Emitting events and properties:
Global
Class 1
Class 2
Resolving local member refs: 0 -> 0 defs, 0 refs, 0 unresolved
Writing PE file
Operation completed successfully

结果,现在再执行程序时,输出是 this is null

this is null

为什么此时 this 是 null

从名字上看,call 是为了调用非虚方法、静态方法或者基类方法的;而 callvirt 是为了调用虚方法的。前者在编译时就将确认调用了某个类的某个方法,而后者将在运行时动态决定应该调用哪个。

然而,当 IL 试图调用某个变量实例的一个方法时,由于不确定这个变量到底是不是实际的类型(还是基类型),所以都采用 callvirt 进行调用。call 在编译时就已确定调用,所以也没有加入 null 的判断;callvirt 却需要,因为通常都是实例使用。

于是,此次便出现了 null.Test() 这样诡异的调用。

一些建议和总结

虽然我们制造出了一个 this 可能为 null 的情况,即便库和调用方是分开开发的,但实际开发中其实并不需要考虑这样的问题。


参考资料

  • Easy way to modify IL code – I know the answer (it’s 42)
  • .net - Call and Callvirt - Stack Overflow
  • Observing a null this value

相关文章:

  • 解决 mklink 使用中的各种坑(硬链接,软链接/符号链接,目录链接)
  • Roslyn 的确定性构建
  • 使用 MSBuild 响应文件 (rsp) 来指定 dotnet build 命令行编译时的大量参数
  • VS 编译太慢了吗?新建解决方案配置关闭一部分项目的编译
  • 流畅设计 Fluent Design System 中的光照效果 RevealBrush,WPF 也能模拟实现啦!
  • 语义版本号(Semantic Versioning)
  • 使用 GitVersion 在编译或持续构建时自动使用语义版本号(Semantic Versioning)
  • UWP 流畅设计中的光照效果(容易的 RevealBorderBrush 和不那么容易的 RevealBackgroundBrush)
  • 使用 Emit 生成 IL 代码
  • 如何快速编写和调试 Emit 生成 IL 的代码
  • 自动将 NuGet 包的引用方式从 packages.config 升级为 PackageReference
  • 冷算法:自动生成代码标识符(类名、方法名、变量名)
  • WPF/UWP 的 Grid 布局竟然有 Bug,还不止一个!了解 Grid 中那些未定义的布局规则
  • Git 更安全的强制推送,--force-with-lease
  • 项目文件中的已知 NuGet 属性(使用这些属性,创建 NuGet 包就可以不需要 nuspec 文件啦)
  • Android Studio:GIT提交项目到远程仓库
  • CSS盒模型深入
  • ES学习笔记(12)--Symbol
  • iOS 颜色设置看我就够了
  • Java新版本的开发已正式进入轨道,版本号18.3
  • js中的正则表达式入门
  • Less 日常用法
  • node和express搭建代理服务器(源码)
  • Quartz实现数据同步 | 从0开始构建SpringCloud微服务(3)
  • Spring声明式事务管理之一:五大属性分析
  • webpack4 一点通
  • 技术胖1-4季视频复习— (看视频笔记)
  • 快速体验 Sentinel 集群限流功能,只需简单几步
  • 聊聊redis的数据结构的应用
  • 区块链分支循环
  • 设计模式(12)迭代器模式(讲解+应用)
  • 使用Tinker来调试Laravel应用程序的数据以及使用Tinker一些总结
  • 微服务框架lagom
  • 优化 Vue 项目编译文件大小
  • Mac 上flink的安装与启动
  • 第二十章:异步和文件I/O.(二十三)
  • 积累各种好的链接
  • ​ ​Redis(五)主从复制:主从模式介绍、配置、拓扑(一主一从结构、一主多从结构、树形主从结构)、原理(复制过程、​​​​​​​数据同步psync)、总结
  • #常见电池型号介绍 常见电池尺寸是多少【详解】
  • (1)(1.11) SiK Radio v2(一)
  • (70min)字节暑假实习二面(已挂)
  • (编程语言界的丐帮 C#).NET MD5 HASH 哈希 加密 与JAVA 互通
  • (附源码)springboot电竞专题网站 毕业设计 641314
  • (剑指Offer)面试题34:丑数
  • (每日持续更新)jdk api之FileFilter基础、应用、实战
  • (全注解开发)学习Spring-MVC的第三天
  • .apk文件,IIS不支持下载解决
  • .NET Core、DNX、DNU、DNVM、MVC6学习资料
  • .Net Core与存储过程(一)
  • .NET Standard 的管理策略
  • .NET 指南:抽象化实现的基类
  • .net6+aspose.words导出word并转pdf
  • .NET高级面试指南专题十一【 设计模式介绍,为什么要用设计模式】
  • []FET-430SIM508 研究日志 11.3.31
  • [2016.7 test.5] T1