【问题标题】:Calling instance method on a null reference in IL在 IL 中的空引用上调用实例方法
【发布时间】:2010-08-10 10:26:07
【问题描述】:

在 IL.. 中可以在空引用上调用实例方法是否正确? 有没有例子可以证明这一点..?

【问题讨论】:

  • 这当然不可能——你会在什么实例上执行该方法?
  • @Adam:有一点 IL 黑客技术是很有可能的。代码仍然是 100% 可验证的,但 C# 编译器永远不会发出这样的代码。
  • 很公平,它如何从空引用中获取并重定向到内存中的正确实例?例如,如果方法修改了内部状态。
  • @Adam:当然,这只有在方法不访问 Hans 和我的回答中提到的任何对象字段时才有效。

标签: c# null cil


【解决方案1】:

是的,这是可能的,只要该方法不使用 this,因为 CLR 不会对 call 指令进行空检查。

您必须手动修改 IL,因为 C# 编译器几乎总是会生成 callvirt 指令1

有关详细信息和示例,请参阅此博客文章:

Instance Methods Called on null References

示例

.method private hidebysig static void  Main(string[] args) cil managed
{
    .entrypoint
    // Code size       18 (0x12)
    .maxstack  1
    .locals init ([0] class SomeClass o, [1] string hello)
    IL_0000:  nop
    IL_0001:  ldnull
    IL_0002:  stloc.0
    IL_0003:  ldloc.0
    IL_0004:  call       instance string SomeClass::GetHello()
    IL_0009:  stloc.1
    IL_000a:  ldloc.1
    IL_000b:  call       void [mscorlib]System.Console::WriteLine(string)
    IL_0010:  nop
    IL_0011:  ret
} 

1事实上,即使在简单的call 指令就足够的情况下,C# 编译器也会发出callvirt 的原因是为了防止在空引用上调用实例方法。通过编译器的这种行为,用户将得到一个NullReferenceException,因此可以避免在空指针上调用方法的奇怪情况。 Eric Gunnerson 前段时间在一篇博文中对此进行了解释:Why does C# always use callvirt? Gishurelated question 中也有很好的解释。

【讨论】:

    【解决方案2】:

    请查看我的blog entry 了解详情。

    IL 代码示例:

    .class Program
    {
      .method static void  Main(string[] args)
      {
        .entrypoint
        .locals init ([0] class Program p)
        ldloc.0 // but wait, it's still null!
        call   instance void Program::Awesome()
        ret
      } 
    
      .method instance void Awesome()
      {
        ldstr      "Awesome!"
        call       void [mscorlib]System.Console::WriteLine(string)
        ret
      } 
    
      .method public specialname rtspecialname instance void  .ctor()
      {
        ldarg.0
        call       instance void [mscorlib]System.Object::.ctor()
        ret
      }
    }
    

    【讨论】:

      【解决方案3】:

      CLR 不需要它,它是语言的实现细节。 C# 和 VB.NET 执行测试。 C++/CLI 是一种著名的托管语言,它允许使用它。只要实例方法不引用任何类成员,就不会出错。如果是这样,NullReferenceException 将照常引发,只是让您很难找出原因。

      #include "stdafx.h"
      
      using namespace System;
      
      ref class Test {
      public:
          void Run() {
              Console::WriteLine("no problem");
          }
      };
      
      int main(array<System::String ^> ^args)
      {
          Test^ obj = nullptr;
          obj->Run();
          return 0;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-11-21
        • 1970-01-01
        • 2012-11-02
        • 1970-01-01
        • 1970-01-01
        • 2021-12-02
        相关资源
        最近更新 更多