(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:
将关注重点放在图中红框标注的部分,那是调用 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 是 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
(C#)if (this == null)?你在逗我,this 怎么可能为 null!用 IL 编译和反编译看穿一切的更多相关文章
- Mysql命令-以NULL做where条件过滤时应该写 IS NULL;
以NULL做where条件过滤时应该写 IS NULL;SELECT * FROM pet WHERE death IS NULL; SELECT * FROM pet WHERE death IS ...
- 特例模式(Special Case Pattern)与空对象模式(Null Pointer Pattern)—— 返回特例对象而非 null
返回 null 值,基本上是在给自己增加工作量,也是给调用者添乱.只有一处没有检查返回的是否为 null,程序就会抛 NullPointerException 异常. 如果你打算在方法中返回 null ...
- 问题:oracle 排序 null值放在最后;结果: ORACLE中null的排序问题
ORACLE中null的排序问题 关键字: oracle nulls 问题描述: 在平时的业务处理中,经常遇到要对业务数据进行排序,并且要对null值也做相应的排序.在Oracle中,进行Ord ...
- 【java】在controller层使用的检查单一字段不能为null和检查属性中某些字段不能为null的工具
========================================================================================= 代码参考地址:Git ...
- “equals”有值 与 “==”存在 “equals”只是比较值是否相同,值传递,==地址传递,null==a,避免引发空指针异常,STRING是一个对象==null,对象不存在,str.equals("")对象存在但是包含字符‘''
原文链接:http://www.cnblogs.com/lezhou2014/p/3955536.html "equals" 与 "==" "equa ...
- scala(一)Nothing、Null、Unit、None 、null 、Nil理解
相对于java的类型系统,scala无疑要复杂的多!也正是这复杂多变的类型系统才让OOP和FP完美的融合在了一起! Nothing: 如果直接在scala-library中搜索Nothing的话是找不 ...
- C# 很少人知道的科技
本文来告诉大家在C#很少有人会发现的科技.即使是工作了好多年的老司机也不一定会知道,如果觉得我在骗你,那么请看看下面 因为C#在微软的帮助,已经从原来很简单的,到现在的很好用.在10多年,很少人知道微 ...
- 2019-11-29-C#-很少人知道的科技
title author date CreateTime categories C# 很少人知道的科技 lindexi 2019-11-29 10:12:43 +0800 2018-03-16 08: ...
- 2019-5-27-C#-很少人知道的科技
title author date CreateTime categories C# 很少人知道的科技 lindexi 2019-05-27 19:33:36 +0800 2018-03-16 08: ...
随机推荐
- nethogs 按进程实时统计网络带宽利用率的工具
安装 sudo apt-get install nethogs 使用 sudo nethogs
- Mysql修改表结构详解
添加字段: alter table `user_movement_log`Add column GatewayId int not null default 0 AFTER `Regionid` (在 ...
- HIVE学习(待更新)
1 安装hive 下载 http://mirrors.shu.edu.cn/apache/hive/hive-1.2.2/,红框中的不需要编译. 由于hive是默认将元数据保存在本地内嵌的 Derby ...
- Codeforces Round #378 (Div. 2)F - Drivers Dissatisfaction GNU
http://codeforces.com/contest/733/problem/F 题意:给你一些城市和一些路,每条路有不满意程度和每减少一点不满意程度的花费,给出最大花费,要求找出花费小于s的最 ...
- constructor&object 的联系与对比
构造函数与对象 构造函数是类中的特殊成员函数,用于为对象分配内存.它可用于为数据成员提供值.创建对象时将调用构造函数.它与类具有相同的名称.构造函数不返回任何值. 构造函数是生成对象的模板,一个构造函 ...
- HDU-4123-树形dp+rmq+尺取
Bob’s Race Time Limit: 5000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- pip 使用总结
pip的安装: Windows Python2.7 以上的版本均自带pip,安装的时候记得勾选对应的选项即可. 安装easy_install, 通过easy_install pip 下载[easy_s ...
- Ansible 小手册系列 八(Yaml 语法格式)
YAML 语言(发音 /ˈjæməl/ )的设计目标,就是方便人类读写.它实质上是一种通用的数据串行化格式. 它的基本语法规则如下. • 大小写敏感 • 使用缩进表示层级关系 • 缩进时不允许使用Ta ...
- hdu4453
题解: splay模板 删除,翻转等等 代码: #include<cstdio> #include<cstring> #include<cmath> #includ ...
- UML_00_资源帖
一.官方文档 https://www.uml-diagrams.org/ https://www.omg.org/spec/UML/ 二.精选资料 UML教程-w3cschool UML建模图实战笔记 ...