web-dev-qa-db-ja.com

(this == null)C#!

C#4で修正されたバグのため、次のプログラムはtrueを出力します。 (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()) { }
}

リリースモードのVS2008では、InvalidProgramExceptionがスローされます。 (デバッグモードでは、正常に動作します)

VS2010 Beta 2では、コンパイルされません(ベータ1は試しませんでした)。私はその難しい方法を学びました

純粋なC#でthis == nullを作成する他の方法はありますか?

129
SLaks

この所見は、StackOverflowの 別の質問 に今日投稿されました。

Marcその質問に対する素晴らしい回答 は、仕様(セクション7.5.7)に従って、thisにアクセスできないことを示します。そのコンテキストと、C#3.0コンパイラでこれを行う機能はバグです。 C#4.0コンパイラは仕様に従って正しく動作しています(ベータ1でも、これはコンパイル時エラーです)。

§7.5.7このアクセス

this-accessは予約語thisで構成されます。

this-access:

this

this-accessは、インスタンスコンストラクター、インスタンスメソッド、またはインスタンスアクセサーのblockでのみ許可されます。

73
Mehrdad Afshari

デバッグモードバイナリの生の逆コンパイル(最適化なしのリフレクタ)は次のとおりです。

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;
    }
}

CompilerGeneratedメソッドは意味がありません。 IL(下)を見ると、nullstring(!)でメソッドを呼び出しています。

   .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 

リリースモードでは、ローカル変数は最適化されて除外されるため、存在しない変数をスタックにプッシュしようとします。

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

(C#に変換するとReflectorがクラッシュします)


[〜#〜] edit [〜#〜]:誰かが(Eric Lippert?)コンパイラがldlocを出力する理由を知っていますか?

24
SLaks

私はそれを持っていました! (そして証拠も得た)

alt text

11
leppie

これは「バグ」ではありません。これは、型システムを乱用しています。現在のインスタンス(this)への参照をコンストラクター内の誰にも渡すことは決してありません。

基本クラスコンストラクター内でも仮想メソッドを呼び出すことで、同様の「バグ」を作成できます。

あなたがcan悪いことをするからといって、それがbugになったからといってそれが意味するわけではありません。

10
Will

私は間違っているかもしれませんが、あなたのオブジェクトがnullである場合、thisが適用されるシナリオは決してないでしょう。

たとえば、CheckNullをどのように呼び出しますか?

Derived derived = null;
Console.WriteLine(derived.CheckNull()); // this should throw a NullReferenceException
4
Dan Tao