9

I was looking through What's the strangest corner case you've seen in C# or .NET?, and this code made me think a little:

public class Program
{
    delegate void HelloDelegate(Strange bar);

    [STAThread()]
    public static void Main(string[] args)
    {
        Strange bar = null;
        var hello = new DynamicMethod("ThisIsNull",
            typeof(void), new[] { typeof(Strange) },
         typeof(Strange).Module);
        ILGenerator il = hello.GetILGenerator(256);
        il.Emit(OpCodes.Ldarg_0);
        var foo = typeof(Strange).GetMethod("Foo");
        il.Emit(OpCodes.Call, foo);
        il.Emit(OpCodes.Ret);
        var print = (HelloDelegate)hello.CreateDelegate(typeof(HelloDelegate));
        print(bar);
        Console.ReadLine();
    }

    internal sealed class Strange
    {
       public void Foo()
       {
           Console.WriteLine(this == null);
       }
    }
}

I do understand what the code does, but I do not understand why it works. Isn't that like doing null.Foo()? It works as if Foo() is static, and this is being called instead: Strange.Foo();.

Would you please tell me what I am missing?

Community
  • 1
  • 1
user3439065
  • 840
  • 5
  • 8

1 Answers1

12

this is implemented as a normal parameter; your code simply passes null as this parameter.

The only reason that this would normally throw a NullReferenceException is that methods are usually called using the CallVirt instruction, which does a vtable lookup on the this parameter and throws if it's null.

If you use call, the method will execute perfectly fine even if this is null, although the method itself will likely end up throwing later.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • 1
    That is very very interesting! Thank you very much for your answer :) I was not aware of the difference between `OpCodes.Callvirt` and `OpCodes.Call`. A small search guided me to this: http://blogs.msdn.com/b/ericgu/archive/2008/07/02/why-does-c-always-use-callvirt.aspx And indeed, in the code above, changing `il.Emit(OpCodes.Call, foo);` to `il.Emit(OpCodes.Callvirt, foo);` throws an exception. Thank you once again! – user3439065 Jul 03 '14 at 19:39
  • 1
    +1. [Why does C# compiler produce method call to call BaseClass method in IL](http://stackoverflow.com/questions/10219188/why-does-c-sharp-compiler-produce-method-call-to-call-baseclass-method-in-il?rq=1) discusses some reasons of using `callvirt` in a bit more details if one is interested. – Alexei Levenkov Jul 03 '14 at 20:13
  • Incidentally, this is why the compiler's use of `call` to implement `base.Foo();` calls is technically a spec violation: the C# spec requires `base.Foo();` to throw a `NullReferenceException` if `this == null`. (Of course, if anyone actually cares to fix that, the fix will probably be to change the spec.) –  Jul 06 '14 at 09:10
  • @hvd: Is there any spec-legal way for `this` to be `null` in the first place? – SLaks Jul 06 '14 at 14:28
  • @SLaks Sure, call it from another method implemented in a language other than C#. The C# spec doesn't require methods not written in C# to adhere to C#'s rules and a large point of .NET is to enable interoperability between different languages. This question is a spec-legal way to get `this == null`. –  Jul 06 '14 at 16:28