Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>I assume you were looking at the .NET 3.5 implementation? I believe the .NET 4 implementation is slightly different.</p> <p>However, I have a sneaking suspicion that this is because it's possible to call even virtual instance methods non-virtually <em>on a null reference</em>. Possible in IL, that is. I'll see if I can produce some IL which would call <code>null.Equals(null)</code>.</p> <p>EDIT: Okay, here's some interesting code:</p> <pre><code>.method private hidebysig static void Main() cil managed { .entrypoint // Code size 17 (0x11) .maxstack 2 .locals init (string V_0) IL_0000: nop IL_0001: ldnull IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: ldnull IL_0005: call instance bool [mscorlib]System.String::Equals(string) IL_000a: call void [mscorlib]System.Console::WriteLine(bool) IL_000f: nop IL_0010: ret } // end of method Test::Main </code></pre> <p>I got this by compiling the following C# code:</p> <pre><code>using System; class Test { static void Main() { string x = null; Console.WriteLine(x.Equals(null)); } } </code></pre> <p>... and then disassembling with <code>ildasm</code> and editing. Note this line:</p> <pre><code>IL_0005: call instance bool [mscorlib]System.String::Equals(string) </code></pre> <p>Originally, that was <code>callvirt</code> instead of <code>call</code>.</p> <p>So, what happens when we reassemble it? Well, with .NET 4.0 we get this:</p> <pre><code>Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object. at Test.Main() </code></pre> <p>Hmm. What about with .NET 2.0?</p> <pre><code>Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object. at System.String.EqualsHelper(String strA, String strB) at Test.Main() </code></pre> <p>Now that's more interesting... we've clearly managed to get into <code>EqualsHelper</code>, which we wouldn't have normally expected.</p> <p>Enough of string... let's try to implement reference equality ourselves, and see whether we can get <code>null.Equals(null)</code> to return true:</p> <pre><code>using System; class Test { static void Main() { Test x = null; Console.WriteLine(x.Equals(null)); } public override int GetHashCode() { return base.GetHashCode(); } public override bool Equals(object other) { return other == this; } } </code></pre> <p>Same procedure as before - disassemble, change <code>callvirt</code> to <code>call</code>, reassemble, and watch it print <code>true</code>...</p> <p>Note that although another answers references <a href="https://stackoverflow.com/questions/2679080/this-null-how-can-it-be-possible">this C++ question</a>, we're being even more devious here... because we're calling a <em>virtual</em> method non-virtually. Normally even the C++/CLI compiler will use <code>callvirt</code> for a virtual method. In other words, I think in this particular case, the only way for <code>this</code> to be null is to write the IL by hand.</p> <hr> <p>EDIT: I've just noticed something... I wasn't actually calling the right method in <em>either</em> of our little sample programs. Here's the call in the first case:</p> <pre><code>IL_0005: call instance bool [mscorlib]System.String::Equals(string) </code></pre> <p>here's the call in the second:</p> <pre><code>IL_0005: call instance bool [mscorlib]System.Object::Equals(object) </code></pre> <p>In the first case, I <em>meant</em> to call <code>System.String::Equals(object)</code>, and in the second, I <em>meant</em> to call <code>Test::Equals(object)</code>. From this we can see three things:</p> <ul> <li>You need to be careful with overloading.</li> <li>The C# compiler emits calls to the <em>declarer</em> of the virtual method - not the most specific <em>override</em> of the virtual method. IIRC, VB works the opposite way</li> <li><code>object.Equals(object)</code> is happy to compare a null "this" reference</li> </ul> <p>If you add a bit of console output to the C# override, you can see the difference - it won't be called unless you change the IL to call it explicitly, like this:</p> <pre><code>IL_0005: call instance bool Test::Equals(object) </code></pre> <p>So, there we are. Fun and abuse of instance methods on null references.</p> <p>If you've made it this far, you might also like to look at my blog post about <a href="http://msmvps.com/blogs/jon_skeet/archive/2008/12/10/value-types-and-parameterless-constructors.aspx" rel="nofollow noreferrer">how value types <em>can</em> declare parameterless constructors</a>... in IL.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload