Вызов метода экземпляра для нулевой ссылки в IL

Верно ли, что метод экземпляра может быть вызван для нулевой ссылки в IL ..? Есть ли какой-нибудь пример, чтобы показать это ..?


person Santhosh    schedule 10.08.2010    source источник
comment
Конечно, это невозможно - на каком экземпляре вы бы выполнили метод?   -  person Adam Houldsworth    schedule 10.08.2010
comment
@ Адам: это вполне возможно с помощью небольшого взлома IL. Код все еще поддается проверке на 100%, но компилятор C # никогда не выдаст такой код.   -  person leppie    schedule 10.08.2010
comment
Достаточно справедливо, как это получить из нулевой ссылки и перенаправить на правильный экземпляр в памяти? Например, если метод изменил внутреннее состояние.   -  person Adam Houldsworth    schedule 10.08.2010
comment
@ Адам: Конечно, это работает только в том случае, если метод не имеет доступа ни к одному из полей объекта, как упоминалось в Гансе и моем ответе.   -  person Dirk Vollmar    schedule 10.08.2010


Ответы (3)


Да, это возможно, если метод не использует this, потому что CLR не выполняет нулевую проверку для call инструкций.

Вам придется изменить IL вручную, поскольку компилятор C # почти всегда генерирует инструкцию callvirt 1.

См. Это сообщение в блоге для получения подробной информации и примера:

Методы экземпляра, вызываемые по пустым ссылкам

Образец

.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 Фактически причина того, что компилятор C # выдает callvirt даже в тех случаях, когда достаточно простой инструкции call, заключается в том, чтобы предотвратить вызов методов экземпляра для нулевых ссылок. Благодаря такому поведению компилятора пользователи получат NullReferenceException, поэтому можно избежать странной ситуации вызова метода для нулевого указателя. Эрик Ганнерсон объяснил это в своем блоге некоторое время назад: Почему C # всегда использует callvirt? Gishu также имеет хорошее объяснение в связанный вопрос.

person Dirk Vollmar    schedule 10.08.2010

См. Мой запись в блоге для информации.

Пример кода 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
  }
}
person leppie    schedule 10.08.2010

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;
}
person Hans Passant    schedule 10.08.2010