开发者

Why is the "this" pointer null in a delegate?

I have the following code (details removed for clarity):开发者_如何学JAVA

    private abstract class Base<TResult> {
        private readonly System.Func<TResult> func = null;

        protected Base(System.Func<TResult> func) {
            this.func = func;
        }

        public TResult Execute() {
            return this.func();
        }
    }

    private class Derived : Base<bool> {
        public Derived(bool myValue) : base(delegate() { return this.MyValue(); }) {
            this.myValue = myValue;
        }

        private bool myValue = false;
        private bool MyValue() {
            return this.myValue; // The "this" pointer is null here...
        }
    }

    Derived d = new Derived(true);
    bool result = d.Execute(); // This results in a null reference pointer (commented above)

Any ideas?

Thanks, Dave


Is that even legal? this is not defined at that point. IIRC, this is a compiler bug - already fixed in 4.0.

Here it is in the 4.0 compiler:

Error 1 Keyword 'this' is not available in the current context C:\Users\Marc\AppData\Local\Temporary Projects\ConsoleApplication1\Program.cs 22 40 ConsoleApplication1

To quote 7.5.7:

A this-access is permitted only in the block of an instance constructor, an instance method, or an instance accessor. It has one of the following meanings:

(emph mine)

...

Use of this in a primary-expression in a context other than the ones listed above is a compile-time error. In particular, it is not possible to refer to this in a static method, a static property accessor, or in a variable-initializer of a field declaration.

In the example given, it it simply invalid.


Using this in a constructor is always dangerous (except in the special case where you are invoking a sibling constructor). Your Derived constructor captures this at the time of its invocation, which is null as the instance hasn't been constructed yet.


It is compiler bug and very weird. Let me explain details. I would be really happy if some experts clarify it.

Yes, it is incorrect to capture this in ctor, but the situation gets hot because this was used inside anonymous delegate. Normally, if anonymous delegate doesn't have closure (doesn't capture outer variables) it is implemented by compiler as static method of same class. It happened here. But let's take a look in IL code generated of that static method:

.method private hidebysig static bool <.ctor>b__0() cil managed
{
    .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()
    .maxstack 1
    .locals init (
        [0] bool CS$1$0000)
    L_0000: nop 
    L_0001: ldloc.0 
    L_0002: call instance bool ConsoleApplication15.Derived::MyValue()
    L_0007: stloc.0 
    L_0008: br.s L_000a
    L_000a: ldloc.0 
    L_000b: ret 
}

did you see that? take closer look at line L_0002 and line L_0001. There are two extremely strange things:

  1. We tried to call method MyValue against bool!
  2. The method was called as call but it is not static, C# compiler usually call instance methods with callvirt! If the callvirt was used this call would fail, because callvirt checks for this == null.

Now let's break it. Let's introduce closure and change code to:

public Derived(bool myValue) : base(delegate() { return myValue ^ this.MyValue(); }) {
    this.myValue = myValue;
}

And now everything is fine! No NRE! Anonymous class was generated and it's fields capture the closure. In this case correct IL is generated:

.method public hidebysig instance bool <.ctor>b__0() cil managed
{
    .maxstack 2
    .locals init (
        [0] bool CS$1$0000)
    L_0000: nop 
    L_0001: ldarg.0 
    L_0002: ldfld bool ConsoleApplication15.Derived/<>c__DisplayClass1::myValue
    L_0007: ldarg.0 
    L_0008: ldfld class ConsoleApplication15.Derived ConsoleApplication15.Derived/<>c__DisplayClass1::<>4__this
    L_000d: call instance bool ConsoleApplication15.Derived::MyValue()
    L_0012: xor 
    L_0013: stloc.0 
    L_0014: br.s L_0016
    L_0016: ldloc.0 
    L_0017: ret 
}

Method is called against closured this. (but still with call instance, hmm)


By looking at your code I would say is a design problem...

Why don't you make the Execute function abstract and let the derived classes provide whatever implementation they want?

For example:

private abstract class Base<TResult> {
    public abstract TResult Execute();
}

private class Derived : Base<bool> {
    //...

    public override bool Execute(){
         return this.myValue;
    }

    //....
}

Derived d = new Derived(true);
bool result = d.Execute(); //This should work now
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜