131

Due to a bug that was fixed in C# 4, the following program prints true. (Try it in LINQPad)

void Main() { new Derived(); }

class Base {
    public Base(Func<string> valueMaker) { Console.WriteLine(valueMaker()); }
}
class Derived : Base {
    string CheckNull() { return "Am I null? " + (this == null); }
    public Derived() : base(() => CheckNull()) { }
}

In VS2008 in Release mode, it throws an InvalidProgramException. (In Debug mode, it works fine)

In VS2010 Beta 2, it doesn't compile (I didn't try Beta 1); I learned that the hard way

Is there any other way to make this == null in pure C#?

Saeed Amiri
  • 22,252
  • 5
  • 45
  • 83
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • 3
    It's most likely a bug in C# 3.0 compiler. It works the way it should in C# 4.0. – Mehrdad Afshari Oct 21 '09 at 13:07
  • Yes, that should not compile at all IMO. – leppie Oct 21 '09 at 13:10
  • what the ...?! Who comes up with such ideas? Nevertheless an interesting bug, thanks for sharing – Marc Wittke Oct 21 '09 at 13:10
  • 83
    @SLaks: Problem with bugs is that you can expect them to be fixed at some point so finding them "useful" is probably not wise. – AnthonyWJones Oct 21 '09 at 13:13
  • It should have been obvious that this was a bug, which should have kept you from relying on it. Hopefully, you didn't rely on it :) – Ryan Emerle Oct 21 '09 at 13:18
  • 6
    thanks! didn't know about LINQPad. it's cool! – thorn0 Oct 21 '09 at 13:19
  • 8
    In what way, exactly, is this useful? – Allen Rice Oct 21 '09 at 13:42
  • 6
    how was this bug useful? – BlackTigerX Oct 21 '09 at 13:42
  • Hence the adage: "It's a feature not a bug." Microsoft is forced to tiptoe its code around developers who rely on such "features", breaking things and keeping other thing that way. – Moshe Apr 26 '10 at 03:14
  • `An object reference is required for the non-static field, method, or property` for both compilers 3.5 and 4.0 under VS2010. What am I doing wrong?? – abatishchev Oct 05 '10 at 19:20
  • 2
    @abatishchev: You're using VS2010. This is a bug in the C# **3.0** compiler. Even if you compile against .Net 2.0 on VS2010, you're still using the C# **4.0** compiler, which fixed the bug. – SLaks Oct 05 '10 at 22:20
  • 1
    @SLaks: Thanks for mentioning! Anyway why can't i access an instance method from nested constructor call? – abatishchev Oct 08 '10 at 16:46
  • @abatishchev: http://msmvps.com/blogs/jon_skeet/archive/2010/09/02/don-t-let-this-get-away.aspx – SLaks Oct 08 '10 at 17:03
  • Wait, how the heck can `this` ever be null?? Sounds like some kind of poltergeist. – Rei Miyasaka Dec 10 '10 at 15:01
  • Also, yeah, the usual way they'd deal with this (at least back when Jason Zander was the self-proclaimed "approver" of all bug-fixes in the CLR/BCL), would have been to leave the bug in in fear of it breaking code that abuses the bug. I for one am glad that they're not sticking to that philosophy as much. – Rei Miyasaka Dec 10 '10 at 15:03
  • possible duplicate of [CS0120 error under vs2010 beta 2 - object reference is required](http://stackoverflow.com/questions/1600001/cs0120-error-under-vs2010-beta-2-object-reference-is-required) – mafu Jan 25 '15 at 07:32

6 Answers6

75

This observation has been posted on StackOverflow in another question earlier today.

Marc's great answer to that question indicates that according to the spec (section 7.5.7), you shouldn't be able to access this in that context and the ability to do so in C# 3.0 compiler is a bug. C# 4.0 compiler is behaving correctly according to the spec (even in Beta 1, this is a compile time error):

§ 7.5.7 This access

A this-access consists of the reserved word this.

this-access:

this

A this-access is permitted only in the block of an instance constructor, an instance method, or an instance accessor.

Community
  • 1
  • 1
Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
  • 2
    I do not see, why in the code presented in this question, usage of keyword "this" is invalid. The method CheckNull is a normal instance method, **nonstatic**. Using "this" is 100% valid in such method, and even comparing this to null is valid. The error is in the base init line: it is the attempt to pass instance-bounded delegate as a parameter to the base ctor. This is the bug (a hole in sematic checks) in the compiler: it should NOT be possible. You are not allowed to write `: base(CheckNull())` if CheckNull is not static, and alike you should not be able to inline an instance-bound lambda. – quetzalcoatl Aug 14 '12 at 00:29
  • 5
    @quetzalcoatl: `this` in `CheckNull` method is legal. What is not legal is the **implicit** *this-access* in `() => CheckNull()`, essentially `() => this.CheckNull()`, which is running outside the **block** of an instance constructor. I agree that the part of spec I cite is mostly focused on the syntactic legality of `this` keyword, and probably another part addresses this issue more precisely, but it is easy to conceptually extrapolate from this part of spec as well. – Mehrdad Afshari Aug 14 '12 at 00:46
  • 2
    Sorry, I disgree. While I know that (and written that in the comment above) and you also know that - you have not mentioned the actual cause of the problem in your (accepted) answer. The answer is accepted - so seemingly the author grapsed it too. But I doubt that all readers will be as bright and fluent in lambdas to recognize a instancebound-lambda versus static-lambda at first sight and map that to 'this' and problems with emitted IL :) This is why I added my three cents. Aside of that, I agree with everything else what was found, analyzed and described by you and others:) – quetzalcoatl Aug 14 '12 at 01:05
24

The raw decompilation (Reflector with no optimizations) of the Debug mode binary is:

private class Derived : Program.Base
{
    // Methods
    public Derived()
    {
        base..ctor(new Func<string>(Program.Derived.<.ctor>b__0));
        return;
    }

    [CompilerGenerated]
    private static string <.ctor>b__0()
    {
        string CS$1$0000;
        CS$1$0000 = CS$1$0000.CheckNull();
    Label_0009:
        return CS$1$0000;
    }

    private string CheckNull()
    {
        string CS$1$0000;
        CS$1$0000 = "Am I null? " + ((bool) (this == null));
    Label_0017:
        return CS$1$0000;
    }
}

The CompilerGenerated method doesn't make sense; if you look at the IL (below), it's calling the method on a null string (!).

   .locals init (
        [0] string CS$1$0000)
    L_0000: ldloc.0 
    L_0001: call instance string CompilerBug.Program/Derived::CheckNull()
    L_0006: stloc.0 
    L_0007: br.s L_0009
    L_0009: ldloc.0 
    L_000a: ret 

In Release mode, the local variable is optimized away, so it tries to push a non-existant variable on to the stack.

    L_0000: ldloc.0 
    L_0001: call instance string CompilerBug.Program/Derived::CheckNull()
    L_0006: ret 

(Reflector crashes when turning it into C#)


EDIT: Does anyone (Eric Lippert?) know why the compiler emits the ldloc?

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
11

I have had that! (and got proof too)

alt text

Community
  • 1
  • 1
leppie
  • 115,091
  • 17
  • 196
  • 297
  • 2
    Was late, was a sign I should stop coding :) Was hacking our with DLR stuff IIRC. – leppie Oct 21 '09 at 17:43
  • make a debugger visualizer (DebuggerDisplay) for whatever 'this' is, and make that fool you that's null? :D just sayin' –  Apr 12 '13 at 09:10
10

This isn't a "bug". This is you abusing the type system. You are never supposed to pass a reference to the current instance (this) to anyone within a constructor.

I could create a similar "bug" by calling a virtual method within the base class constructor as well.

Just because you can do something bad doesn't mean its a bug when you get bit by it.

  • 14
    It is a compiler bug. It generates invalid IL. (Read my answer) – SLaks Oct 21 '09 at 13:11
  • The context is static, so you should not be allowed an instance method reference at that stage. – leppie Oct 21 '09 at 13:12
  • You do something you should never do, then shiz breaks. And that's a compiler bug? Again, there are things you can do that you aren't supposed to do which will break, it doesn't mean that it is a bug because the compiler couldn't handle your acting badly. –  Oct 21 '09 at 13:16
  • 10
    @Will: It's a compiler bug. The compiler is supposed to generate *valid*, *verifiable* code for that code snippet or spit out an error message. When a compiler does not behave according to the spec, **it is buggy**. – Mehrdad Afshari Oct 21 '09 at 13:22
  • The thing is if this is "fixed" in 4.0 then it is still broken, as `this` can never equal `null`. Maybe it has to do with their shoehorning in the DLR? –  Oct 21 '09 at 13:23
  • @Will: "The thing is if this is "fixed" in 4.0 then it is still broken, as `this` can never equal `null`." Isn't it how it should be? Do you ever check `if (this != null)` in instance methods? C# programmers rely on `this` not being `null`. The old behavior did not implement the spec correctly. – Mehrdad Afshari Oct 21 '09 at 13:27
  • 2
    @Will#4: When I wrote the code, I hadn't thought about the implications. I only realized that it didn't make sense when it stopped compiling in VS2010. – – SLaks Oct 21 '09 at 13:48
  • @Will#3: What on earth do you mean? – SLaks Oct 21 '09 at 13:48
  • 3
    By the way, the virtual method call in constructor is a completely valid operation. It's just not recommended. It *may* result in logical disasters but never an `InvalidProgramException`. – Mehrdad Afshari Oct 21 '09 at 14:02
  • I try to stay away from valid operations that can result in logical disasters, heheh. –  Oct 21 '09 at 14:42
3

I could be wrong, but I'm pretty sure if your object is null there's never going to be a scenario where this applies.

For instance, how would you call CheckNull?

Derived derived = null;
Console.WriteLine(derived.CheckNull()); // this should throw a NullReferenceException
Dan Tao
  • 125,917
  • 54
  • 300
  • 447
  • 3
    In a lambda in the constructor argument. Read the entire code snippet. (And try it if you don't believe me) – SLaks Oct 21 '09 at 13:01
  • I agree although I do remember faintly something about how in C++ an object didn't have reference within it's constructor and I'm wondering if the (this == null) scenario is used in those cases to check whether a call to a method was made from the object's constructor before exposing a pointer to "this". Though, as far as I know in C#, there shouldn't be any cases where "this" would ever be null, not even in the Dispose or finalization methods. – jpierson Oct 21 '09 at 13:05
  • I guess my point is that the very idea of `this` is mutually exclusive of the possibility of being null--sort of a "Cogito, ergo sum" of computer programming. Therefore your desire to use the expression `this == null` and ever have it return true strikes me as misguided. – Dan Tao Oct 21 '09 at 14:19
  • In other words: I did read your code; what I'm saying is that I question what you were trying to accomplish in the first place. – Dan Tao Oct 21 '09 at 14:20
  • This code simply demonstrates the bug, and, as you pointed out, is utterly useless. To see real useful code, read my second answer. – SLaks Oct 21 '09 at 16:23
-1

Not sure if this is what you are looking for

    public static T CheckForNull<T>(object primary, T Default)
    {
        try
        {
            if (primary != null && !(primary is DBNull))
                return (T)Convert.ChangeType(primary, typeof(T));
            else if (Default.GetType() == typeof(T))
                return Default;
        }
        catch (Exception e)
        {
            throw new Exception("C:CFN.1 - " + e.Message + "Unexpected object type of " + primary.GetType().ToString() + " instead of " + typeof(T).ToString());
        }
        return default(T);
    }

example: UserID = CheckForNull(Request.QueryString["UserID"], 147);

Scott and the Dev Team
  • 2,403
  • 5
  • 28
  • 40