(this == null)C#で!


129

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では、コンパイルされません(私はBeta 1を試していません)。私は難しい方法を学びました

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


3
C#3.0コンパイラのバグである可能性が高いです。それはC#4.0で必要な方法で動作します。
Mehrdad Afshari、

82
@SLaks:バグの問題は、それらがいつか修正されることを期待できるため、それらを「有用」と見つけることはおそらく賢明ではないということです。
AnthonyWJones

6
ありがとう!LINQPadについて知りませんでした。かっこいいね!
ソーン̈2009年

8
正確には、これはどのように役立ちますか?
アレンライス

6
このバグはどのように役に立ちましたか?
BlackTigerX 2009年

回答:


73

この観察は、今日の別の質問でStackOverflowに投稿されています。

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

§7.5.7このアクセス

このアクセスは予約語で構成さthis

this-access:

this

このアクセスは、のみに許可されているブロックインスタンスコンストラクタ、インスタンスメソッド、またはインスタンスアクセサの。


2
この質問で提示されたコードで、キーワード「this」の使用が無効である理由はわかりません。方法CheckNullは、通常のインスタンスメソッド、ある非静的。そのようなメソッドでは「this」の使用は100%有効であり、これをnullと比較することも有効です。エラーはベースのinit行にあります。これは、インスタンスバウンドのデリゲートをパラメーターとしてベースctorに渡そうとする試みです。これはコンパイラーのバグ(セマティックチェックの穴)です。それは可能ではありません。: base(CheckNull())CheckNullが静的でない場合、書き込みは許可されません。同様に、インスタンスにバインドされたラムダをインライン化することはできません。
ケツァルコアトル2012

4
@quetzalcoatl:thisCheckNull方法が合法です。正しくないのは、本質的にでの暗黙の thisアクセスであり、インスタンスコンストラクタのブロックの外側で実行されています。私が引用する仕様の一部はキーワードの構文的合法性に主に焦点を当てていることに同意し、おそらく別の部分がこの問題をより正確に扱いますが、仕様のこの部分から概念的に推測することも簡単です。() => CheckNull()() => this.CheckNull()this
Mehrdad Afshari 2012

2
すみません。私はそれを知っています(そして上記のコメントでそれを書いています)とあなたはそれも知っています-あなたは(受け入れられた)回答で問題の実際の原因について言及していません。答えは受け入れられました-どうやら著者もそれを掴んだようです。しかし、すべての読者がインスタンスバインドされたラムダとスタティックラムダを一目で認識し、それを「これ」と放出されたILの問題にマッピングするために、ラムダ語をそれほど流暢に流暢に話せるとは思えません。これが、3セントを追加した理由です。それはさておき、私はあなたや他の人々によって発見され、分析され、記述された他のすべてに同意します:)
ケツァルコアトル2012

24

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

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(下記)を見ると、null 文字列(!)でメソッドが呼び出されています。

   .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#に変換するとクラッシュします)


編集:コンパイラ(Eric Lippert?)がコンパイラを発行する理由を知っていldlocますか?


11

食べました!(そして証明も得た)

代替テキスト


2
遅くなりました、私はコーディングをやめるべきだという兆候でした:) DLRものIIRCで私たちをハッキングしていました。
leppie 2009年

'this'が何であれ、デバッガビジュアライザ(DebuggerDisplay)を作成し、それをnullだと騙しますか?:Dは、単にコト

10

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

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

あなた何か悪いことをすることができるからといって、それを少しでも理解したときにそれがバグであることを意味するわけではありません。


14
コンパイラのバグです。無効なILを生成します。(私の答えをお読みください)
SLaks、2009年

コンテキストは静的であるため、その段階ではインスタンスメソッド参照を許可しないでください。
leppie

10
@Will:これはコンパイラのバグです。コンパイラは、そのコードスニペットに対して有効検証可能なコードを生成するか、エラーメッセージを出力することになっています。コンパイラが仕様どおりに動作しない場合、バグがあります。
Mehrdad Afshari、

2
@ Will#4:私がコードを書いたとき、私はその影響について考えていませんでした。VS2010でコンパイルを停止しても意味がないことに気づきました。–
SLaks 2009年

3
ちなみに、コンストラクタでの仮想メソッド呼び出しは完全に有効な操作です。それは単にお勧めできません。それ論理的な災害をもたらすかもしれませんが、決してありませんInvalidProgramException
Mehrdad Afshari、

3

私は間違っている可能性がnullありますが、あなたのオブジェクトがthis適用されるシナリオになることは決してないだろうと私は確信しています。

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

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

3
コンストラクター引数のラムダ内。コードスニペット全体をお読みください。(そして、あなたが私を信じないならそれを試してください)
SLaks

私は同意しますが、C ++では、オブジェクトがコンストラクター内で参照を持たなかった方法について何かを覚えていますが、(this == null)シナリオを使用して、メソッドへの呼び出しが行われたかどうかを確認しているのでしょうか「this」へのポインタを公開する前に、オブジェクトのコンストラクタから作成されます。ただし、C#で知る限り、Disposeメソッドやfinalizationメソッドでも、「this」がnullになることはありません。
jpierson 2009年

私のポイントは、まさにその考えが、thisnullになる可能性を相互に排他的であるということだと思います-コンピュータプログラミングの「コギト、エルゴサム」のようなものです。したがって、その表現を使用して、this == nullそれが本当の結果を返すようにしたいというあなたの願望は、見当違いのように私を襲います。
ダン・タオ

言い換えれば、私はあなたのコードを読みました。私が言っているのは、そもそもあなたが何を成し遂げようとしていたのかということです。
ダン・タオ

このコードは単にバグを示しており、ご指摘のとおり、まったく役に立ちません。実際に役立つコードを確認するには、2番目の答えを読んでください。
2009年

-1

これがあなたが探しているものかどうかわかりません

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

例:UserID = CheckForNull(Request.QueryString ["UserID"]、147);


13
あなたはその質問を完全に誤解しました。
10

1
私も思いつきました。とにかくやってみようかな。
スコットと開発チーム
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.