13

I was curious if there is a way for this to be null in a virtual method in C#. I assume it is not possible. I saw that in existing code, during a code review and I would like to be 100% sure to comment for its removal, but I would like some confirmation and some more context from the community. Is it the case that this != null in any non-static / instance method? Otherwise it would have been a null pointer exception right? I was thinking of extension methods and such or any C# feature that I could possibly be not familiar with coming from years of Java.

Michail Michailidis
  • 11,792
  • 6
  • 63
  • 106
  • 1
    Writing C# code, `this != null` always... Not sure if by writing directly IL code you can bypass the check... – xanatos Jul 31 '15 at 13:48
  • [here](http://stackoverflow.com/a/193952/613130) there is a comment about this: *If I remember correctly call doesn't check the pointer against null before performing the call, something which callvirt obviously needs to* – xanatos Jul 31 '15 at 13:49
  • @LasseV.Karlsen extension methods can't be virtual though? Can they? – Michail Michailidis Jul 31 '15 at 13:50
  • 1
    Ugh, I completely botched that comment. What I meant was. The `this` parameter sent to an extension method is allowed to be `null`, but using `this` inside the method will give a compiler error, not legal to use it. – Lasse V. Karlsen Jul 31 '15 at 13:50
  • @LasseV.Karlsen so it won't even compile if I try to access this in a static method right? – Michail Michailidis Jul 31 '15 at 13:51
  • 2
    See my updated comment, not sure what I was thinking when I wrote the first one. – Lasse V. Karlsen Jul 31 '15 at 13:51
  • 1
    As for the comparison question - read https://stackoverflow.com/questions/3507383/equalsitem-null-or-item-null – DeJaVo Jul 31 '15 at 13:54
  • It's unclear what you mean when you "saw that during code review." You saw what exactly? A check that `this != null`? Code based on the assumption that it can't be `null`? Please clarify in the question. (Alternatively, you could just remove that note from the question.) – jpmc26 Aug 01 '15 at 00:25

6 Answers6

14

It's not standard C# but, further to the answers from Lasse and Jon, with a bit of IL-fiddling you can make a non-virtual call (to either virtual or non-virtual methods) passing a null this:

using System;
using System.Reflection.Emit;

class Test
{
    static void Main()
    {
        CallWithNullThis("Foo");
        CallWithNullThis("Bar");
    }

    static void CallWithNullThis(string methodName)
    {
        var mi = typeof(Test).GetMethod(methodName);

        // make Test the owner type to avoid VerificationException
        var dm = new DynamicMethod("$", typeof(void), Type.EmptyTypes, typeof(Test));
        var il = dm.GetILGenerator();
        il.Emit(OpCodes.Ldnull);
        il.Emit(OpCodes.Call, mi);
        il.Emit(OpCodes.Ret);

        var action = (Action)dm.CreateDelegate(typeof(Action));
        action();
    }

    public void Foo()
    {
        Console.WriteLine(this == null ? "Weird" : "Normal");
    }

    public virtual void Bar()
    {
        Console.WriteLine(this == null ? "Weird" : "Normal");
    }
}
Community
  • 1
  • 1
LukeH
  • 263,068
  • 57
  • 365
  • 409
  • You can "by pass" the compiler using Reflection/IL ... but still this is not the normal way C# code gets compiled. And as you are doing this by pass is **your** responsability to check for nulls. – Fernando Callejon Aug 02 '15 at 18:55
8

It's not possible to do this in normal C# (i.e. calling a method or property in a normal way), regardless of whether the method is virtual or not.

For non-virtual methods, you can create a delegate from an open instance method, effectively treating the instance method as a static method with a first parameter with the target type. You can invoke that delegate with a null argument, and observe this == null in the method.

For a virtual method, you'd have to call the method non-virtually - which can happen with a call such as base.Foo(...)... but I'm not sure whether there's any way of making that sort of non-virtual call with a null argument. The delegate approach definitely doesn't work here.

Demo code:

using System;

class Test
{
    static void Main()
    {
        Action<Test> foo = (Action<Test>)
            Delegate.CreateDelegate(typeof(Action<Test>), typeof(Test).GetMethod("Foo"));
        foo(null); // Prints Weird
        Action<Test> bar = (Action<Test>)
            Delegate.CreateDelegate(typeof(Action<Test>), typeof(Test).GetMethod("Bar"));

        bar(null); // Throws
    }

    public void Foo()
    {
        Console.WriteLine(this == null ? "Weird" : "Normal");
    }

    public virtual void Bar()
    {
        Console.WriteLine(this == null ? "Weird" : "Normal");
    }
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • "but I'm not sure whether there's any way of making that sort of non-virtual call with a null argument" -- Write `base.Foo(...)` in a non-virtual method, and call that non-virtual method with `null` as `this`. :) –  Jul 31 '15 at 14:18
  • 3
    You can call `Bar` non-virtually by creating the delegate using `DynamicMethod`. See [my answer](http://stackoverflow.com/a/31749049/55847) for an example. – LukeH Jul 31 '15 at 14:51
7

It's not possible for this to be null in a virtual call. If you have a null reference then you don't have an instance of an object, and if you don't have an instance of an object then it's not possible to get the type of the object to determine which virtual method to call.

In an non-virtual method this can't be null either, but that's because the compiler won't let you make the call, it would be theoretically possible to make the call. You can call an extension method on a null reference though, which will make the this parameter null.

Similarly, it's theoretically possible to make a non-virtual call to a virtual method, using a null reference. Specifying the exact method, and using reflection it might be possible to get around the limitations in the compiler and call the method with a null reference.

Guffa
  • 687,336
  • 108
  • 737
  • 1,005
  • How about a non-virtual call to a virtual method, as per `base.Foo(...)`? Trying to work out how to demonstrate that... – Jon Skeet Jul 31 '15 at 13:55
  • No, my comment was in reference to the first sentence in your answer. – Jon Skeet Jul 31 '15 at 13:57
  • @JonSkeet If your method's been called as base.Foo, it's being called from another function which was called with a virtual call on the same `this`. – Random832 Jul 31 '15 at 20:16
  • @Random832: Not necessarily a virtual call... You could be in a nonvirtual method where this is null. May try that later... – Jon Skeet Jul 31 '15 at 20:24
  • You can be in a non virtual method, but if it was called from c# it was a virtual call. – Random832 Jul 31 '15 at 21:56
3

Not possible directly in C# code (unless you generate dynamic code with Reflection.Emit or similar techniques) but by using directly IL code it is possible to call a virtual method and having the this == null.

Take this code:

using System;

public class C {
    public virtual void M() {
        Console.WriteLine("Inside the method M. this == null: {0}", this == null);
    }
}

public class Program {
    public static void Main(string[] pars)
    {
        C obj = null;
        obj.M();
    }
}

save it to testnull.cs. From a Visual Studio command prompt, do:

csc.exe testnull.cs

ildasm.exe testnull.exe /out:testnull.il

then look in the testnull.il to this line of code:

callvirt   instance void C::M()

and change it to:

call   instance void C::M()

and save.

ilasm.exe testnull.il /out:testnull2.exe

now try running it:

testnull2.exe

and you'll get:

Inside the method M. this == null: True

if you want, the full il code is:

//  Microsoft (R) .NET Framework IL Disassembler.  Version 4.0.30319.33440
//  Copyright (c) Microsoft Corporation.  All rights reserved.



// Metadata version: v4.0.30319
.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
  .ver 4:0:0:0
}
.assembly testnull
{
  .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) 
  .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78   // ....T..WrapNonEx
                                                                                                             63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 )       // ceptionThrows.
  .hash algorithm 0x00008004
  .ver 0:0:0:0
}
.module testnull.exe
// MVID: {D8510E3B-5C38-40B9-A5A2-7DAE75DE1642}
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003       // WINDOWS_CUI
.corflags 0x00000001    //  ILONLY
// Image base: 0x00300000


// =============== CLASS MEMBERS DECLARATION ===================

.class public auto ansi beforefieldinit C
       extends [mscorlib]System.Object
{
  .method public hidebysig newslot virtual 
          instance void  M() cil managed
  {
    // Code size       22 (0x16)
    .maxstack  8
    IL_0000:  nop
    IL_0001:  ldstr      "Inside the method M. this == null: {0}"
    IL_0006:  ldarg.0
    IL_0007:  ldnull
    IL_0008:  ceq
    IL_000a:  box        [mscorlib]System.Boolean
    IL_000f:  call       void [mscorlib]System.Console::WriteLine(string,
                                                                  object)
    IL_0014:  nop
    IL_0015:  ret
  } // end of method C::M

  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // Code size       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  ret
  } // end of method C::.ctor

} // end of class C

.class public auto ansi beforefieldinit Program
       extends [mscorlib]System.Object
{
  .method public hidebysig static void  Main(string[] pars) cil managed
  {
    .entrypoint
    // Code size       11 (0xb)
    .maxstack  1
    .locals init (class C V_0)
    IL_0000:  nop
    IL_0001:  ldnull
    IL_0002:  stloc.0
    IL_0003:  ldloc.0
    IL_0004:  call   instance void C::M()
    IL_0009:  nop
    IL_000a:  ret
  } // end of method Program::Main

  .method public hidebysig specialname rtspecialname 
          instance void  .ctor() cil managed
  {
    // Code size       7 (0x7)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
    IL_0006:  ret
  } // end of method Program::.ctor

} // end of class Program


// =============================================================

// *********** DISASSEMBLY COMPLETE ***********************
// WARNING: Created Win32 resource file testnull.res

Note that the code that must be written in IL code is the calling code (the code that does the call), not the called code (the virtual method that is called).

In the initial versions of the C# compiler (probably internal pre-alpha versions of C# 1.0... the change I'm speaking here was done at the end of 1999, while C# 1.0 was released in 2002), Microsoft programmers tried to sometimes generate call methods instead of callvirt methods (call calls don't do null checks, while callvirt calls do it), but after discovering that it was possible to make a call to an instance method having a this == null, they decided for always using callvirt for instance methods (see here).

Community
  • 1
  • 1
xanatos
  • 109,618
  • 12
  • 197
  • 280
  • 3
    Note also that by doing that the call is no longer virtual. It's not possible to make an actual virtual call without a reference to an object. – Guffa Jul 31 '15 at 14:13
2

It is actually possible for this to be null in an instance method.

Here is a short LINQPad program that demonstrates:

void Main()
{
    var method = typeof(Test).GetMethod("Method");

    var d = new DynamicMethod("xx", typeof(void), new Type[0]);
    var il = d.GetILGenerator();
    il.Emit(OpCodes.Ldnull);
    il.Emit(OpCodes.Call, method);
    il.Emit(OpCodes.Ret);

    var a = (Action)d.CreateDelegate(typeof(Action));
    a();
}

public class Test
{
    public void Method()
    {
        this.Dump();
    }
}

Output:

null

Basically I make the call directly to the method with a null-reference on the stack. I doubt the C# compiler will actually create code like this but since it is possible, it can happen.

Now as for the rest:

  • To make a virtual call, you need to go through the virtual method table, you need an instance for this, so no, this cannot be null in a virtual method
  • An extension method is a static method, and static methods don't have a this to begin with.

I tested the above code with a virtual method as well and got this exception when calling a():

VerificationException
Operation could destabilize the runtime.

Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
1

This is an odd question, really. A virtual method is an instance method, so this is an instace of an object.

You cannot use the virtual modifier with the static, abstract, private, or override modifiers.

MSDN: https://msdn.microsoft.com/en-us/library/9fkccyh4.aspx

The IDE will tell you why too

  • I didn't try to put static with virtual.. I was trying to see if there is a way for this to be null in any way, because I saw that in some code review and I wanted to comment on that but not sure if C# has some crazy feature to bind methods to objects on the fly or something like that – Michail Michailidis Jul 31 '15 at 13:55
  • Ok... then it will be useful in extension methods. But you won't have "this" but the first parameter name. ie.: `public static bool (this ClassName myThis) { return myThis != null; } ` – Fernando Callejon Jul 31 '15 at 14:02
  • 1
    this happens because IL uses "call" not "callvirt" http://www.hanselman.com/blog/HowDoExtensionMethodsWorkAndWhyWasANewCLRNotRequired.aspx – Fernando Callejon Jul 31 '15 at 14:09
  • 1
    Not that odd a question for someone coming from a C++ background, where `this` can indeed be null. (Generally the result of calling a function on a bad pointer.) Actually makes me wonder if this would be possible in C# if running inside an `unsafe` block... – Darrel Hoffman Jul 31 '15 at 21:22
  • Well he has Java background and the question is about calling a virtual method, not a challenge to call a method having a `null` `this`. I still think the answers is: no, you can't. Extension methods are static methods therefore can't be virtual. – Fernando Callejon Aug 02 '15 at 18:44