views:

187

answers:

4

In the Framework we are building we need the following pattern:

public class BaseRenderer
{
    Func<string> renderer;
    public BaseRenderer(Func<string> renderer)
    {
        this.renderer = renderer;
    }

    public string Render()
    {
        return renderer();
    }
}

public class NameRenderer : BaseRenderer
{
    public string Name{ get; set; }

     public NameRenderer ()
        : base(() =>this.Name)
     {}
}

As you see, we create a lambda when calling the base constructor.

public class Program
{
    public static void Main()
    {
        Console.WriteLine(new NameRenderer(){Name = "Foo"}.Render());
    }
}

Oddly, when trying to actually use the lambda it throws NullReferenceException (Console Application), or some kind of ExecutionEngineExceptionexception (Web app on IIS).

I think the reason is that this pointer is not ready before calling base constructor, so the lambda is unable to capture this.Name at this stage.

Shouldn't it throw an exception in "capture time" instead of "execution time"? Is this behavior documented?

I can refactor the code in a different way, but I think it worths a comment.

+3  A: 

I think you are right. The subclass is not yet constructed when the base class constructor is called and therefore accessing members on the subclass gives you a null reference. CLR doesn't have a way to know at compile time if the instance exists or not.

Moving the logic to the constructor body should fix the problem.

darthjit
+5  A: 

The C# specification at 7.5.7 says: "A this-access is permitted only in the block of an instance constructor, an instance method, or an instance accessor."

And even more directly in 10.11.1: "An instance constructor initializer cannot access the instance being created. Therefore it is a compile-time error to reference this in an argument expression of the constructor initializer, as is it a compile-time error for an argument expression to reference any instance member through a simple-name."

Though the instance has been created according to 7.5.10.

Hmm. That's actually pretty strange. I did not see any compile-time error.

asgerhallas
I was not quite right the first time. There is an instance at the time, but you have no access to it. The answers is updated.
asgerhallas
You did not see a compilation error because the compiler had a bug. It should have been an error, and in C# 4, it is.
Eric Lippert
+14  A: 

As asgerhallas correctly points out, this should not be legal according to the specification. We accidentally allowed this bogus usage to sneak by the error detector that searches for incorrect usages of "this" before it is legal to do so. I've fixed the bug; the C# 4 compiler correctly flags your program as an error.

Many apologies for the inconvenience; this was my mistake.

Eric Lippert
hah, not often that a) you come across a bug in the compiler, and b) the dev responsible responds to your question with a mea culpa :-P sweet!
Joel Martinez
The C# Team is amazing, thanks Eric.
Olmo
+2  A: 

The lambda captured the value of "this" and captured null since the object wasn't constructed yet. This strikes me as a compiler bug, it should have generated an error for this. Code like this normally generates a CS0027 (keyword 'this' is not available in the current context) or CS0120 (an object reference is required). I bet that isn't easy to implement.

Anyhoo, the code cannot work. The NameRenderer class needs a constructor with a string argument so it can initialize the base class.

Hans Passant