(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: ...
随机推荐
- Win10配Theano环境和Keras框架
网络上有各种各样的win7 64bit安装theano的方法,我也试过好多,各种各样的问题.因为之前没了解过MinGw等东西,所以安装起来比较费劲,经过不断的尝试,最终我按照以下过程安装成功. 其实过 ...
- SpringMvc中的校验框架@valid和@validation的概念及相关使用 和BindingResult bindingResult
1.比较 @Valid是使用hibernate validation的时候使用 @Validated 是只用spring Validator 校验机制使用\ 2.实现 其中,@valid,java的 ...
- sqlserver存储过程杀掉数据库中死锁
Create proc p_lockinfo @kill_lock_spid bit=1, --是否杀掉死锁的进程,1 杀掉, 0 仅显示 @show_spid_if_nolock ...
- 1-15-1 RAID磁盘阵列的原理和搭建
大纲: 1.1-1-企业级RAID磁盘阵列 RAID磁盘阵列的原理 RAID0,1,5,10的搭建 硬件RAID卡 1.2-1-使用廉价的磁盘搭建RAID磁盘阵列 实战-配置RAID0带区卷 ==== ...
- python----tkinterm模块
python tkinter学习——布局 目录 一.pack() 二.grid() 三.place() 四.Frame() 正文 布局 一.pack() pack()有以下几个常用属性: side ...
- Loadrunner 11检查点使用方法总结
在使用Loadrunner 11进行性能测试中,有时需要对性能测试中的功能是否全部正确进行判断.这里就需要用到“检查点”,本文总结了常用三种协议下检查点的使用方法,希望阅读本文后的小伙伴们能够掌握其使 ...
- 十四、dbms_obfuscation_toolkit(用于加密和解密应用数据)
1.概述 作用:用于加密和解密应用数据,另外还可以生成密码检验和.通过加密输入数据,可以防止黑客或其他用户窃取私有数据;而通过结合使用加密和密码检验和,可以防止黑客破坏初加密的数据.当使用该包加密数据 ...
- CF911D
题解: 简单的奇偶判断 代码: #include<bits/stdc++.h> using namespace std; ; int n,a[N],ans,m,p,q; int main( ...
- HDU 2669 Romantic (扩展欧几里得定理)
Romantic Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Su ...
- 关于px em rem的一点小总结
2015-11-28 06:06:40 概念 都是CSS单位. px:像素 Pixel.像素 (计算机屏幕上的一个点) em:1em 等于当前的字体尺寸. 2em 等于当前字体尺寸的两倍. 例如,如果 ...