我有以下代码(为清晰起见,删除了详细信息):
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)
有什么想法吗?
谢谢,戴夫
答案 0 :(得分:6)
甚至合法?此时未定义this
。 IIRC,这是一个编译器错误 - 已在4.0中修复。
这是在4.0编译器中:
错误1关键字'this'在当前上下文中不可用C:\ Users \ Marc \ AppData \ Local \ Temporary Projects \ ConsoleApplication1 \ Program.cs 22 40 ConsoleApplication1
引用7.5.7:
仅允许此访问权限在实例构造函数的块中,实例方法或实例访问者。它具有以下含义之一:
(emph mine)
...
在上面列出的上下文中的primary-expression中使用this是编译时错误。特别是,无法在静态方法,静态属性访问器或字段声明的变量初始化程序中引用它。
在给出的例子中,它只是无效。
答案 1 :(得分:5)
在构造函数中使用this
总是很危险的(除非在您调用兄弟构造函数的特殊情况下)。您的Derived
构造函数在调用时捕获this
,因为尚未构造实例,所以它为null。
答案 2 :(得分:1)
这是编译器错误,非常奇怪。让我解释一下细节。如果有专家澄清,我会非常高兴。
是的,在ctor中捕获this
是不正确的,但情况变得很热,因为在匿名委托中使用了this
。通常,如果匿名委托没有闭包(不捕获外部变量),则它由编译器实现为同一类的静态方法。它发生在这里。但是让我们看一下由该静态方法生成的IL代码:
.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
}
你看到了吗?仔细查看第L_0002
行和第L_0001
行。有两个非常奇怪的事情:
MyValue
!bool
call
,但它不是静态的,C#编译器通常使用callvirt
调用实例方法!如果使用了callvirt
,则此调用将失败,因为callvirt
会检查this == null
。现在让我们打破它。让我们介绍闭包和更改代码:
public Derived(bool myValue) : base(delegate() { return myValue ^ this.MyValue(); }) {
this.myValue = myValue;
}
现在一切都很好!没有NRE!生成了匿名类,它的字段捕获了闭包。在这种情况下,生成正确的IL:
.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
}
针对关闭调用此方法。 (但仍然使用call instance
,嗯)
答案 3 :(得分:0)
通过查看您的代码,我会说这是一个设计问题......
为什么不将Execute
函数抽象化,让派生类提供他们想要的任何实现?
例如:
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