35

thisインスタンスメソッド本体内で が null になることは不可能だといつも思っていました。次の簡単なプログラムは、それが可能であることを示しています。これは文書化された動作ですか?

class Foo
{
    public void Bar()
    {
        Debug.Assert(this == null);
    }
}

public static void Test()
{            
    var action = (Action)Delegate.CreateDelegate(typeof (Action), null, typeof(Foo).GetMethod("Bar"));
    action();
}

アップデート

この方法がどのように文書化されているかという答えに同意します。しかし、私はこの行動をよく理解していません。特に、それは C# の設計方法ではないためです。

Null ポインターでメソッドを呼び出すコードを書いた誰か (おそらく C# を使用している .NET グループの 1 つ (当時はまだ C# という名前ではなかったと思われます)) からレポートを受け取りましたが、彼らは取得しませんでした。メソッドがどのフィールドにもアクセスしなかったため、例外が発生しました (つまり、「this」は null でしたが、メソッドでは何も使用されませんでした)。そのメソッドは、this ポイントを使用する別のメソッドを呼び出して例外をスローし、頭を悩ませました。彼らがそれを理解した後、彼らは私たちにそれについてのメモを送った. null インスタンスでメソッドを呼び出すことができるのは少し奇妙だと思いました。Peter Golde は、常に callvirt を使用した場合のパフォーマンスへの影響を確認するためにいくつかのテストを行いました。

http://blogs.msdn.com/b/ericgu/archive/2008/07/02/why-does-c-always-use-callvirt.aspx

4

5 に答える 5

23

あなたがのに渡しnullているのでfirstArgumentDelegate.CreateDelegate

したがって、nullオブジェクトでインスタンスメソッドを呼び出しています。

http://msdn.microsoft.com/en-us/library/74x8f551.aspx

firstArgumentがnull参照であり、methodがインスタンスメソッドである場合、結果はデリゲート型タイプとメソッドのシグネチャによって異なります。

タイプのシグニチャにメソッドの非表示の最初のパラメータが明示的に含まれている場合、デリゲートはオープンインスタンスメソッドを表すと言われます。デリゲートが呼び出されると、引数リストの最初の引数がメソッドの非表示インスタンスパラメーターに渡されます。

メソッドとタイプのシグネチャが一致する場合(つまり、すべてのパラメータータイプに互換性がある場合)、デリゲートはnull参照で閉じられていると言われます。デリゲートを呼び出すことは、nullインスタンスでインスタンスメソッドを呼び出すようなものですが、これは特に便利なことではありません。

于 2012-05-16T19:30:23.470 に答える
12

コールIL命令またはデリゲートアプローチを使用している場合は、メソッドを呼び出すことができます。このブービートラップをオフに設定するのは、探していたNullReferenceExceptionを与えるメンバーフィールドにアクセスしようとした場合のみです。

試す

 int x;
 public void Bar()
 {
        x = 1; // NullRefException
        Debug.Assert(this == null);
 }

BCLには、callvirt(C#など)を常に使用しない言語のデバッグを支援するために、明示的なthis==nullチェックも含まれています。詳細については、この質問を参照してください。

たとえば、Stringクラスにはそのようなチェックがあります。C#のような言語でそれらの必要性が見られないことを除いて、それらについて不思議なことは何もありません。

// Determines whether two strings match. 
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] 
public override bool Equals(Object obj)
{
    //this is necessary to guard against reverse-pinvokes and
    //other callers who do not use the callvirt instruction
    if (this == null)
        throw new NullReferenceException();

    String str = obj as String;
    if (str == null) 
        return false;

    if (Object.ReferenceEquals(this, obj)) 
        return true;

    return EqualsHelper(this, str);
}
于 2012-05-16T19:30:17.103 に答える
5

のドキュメントをDelegate.CreateDelegate() msdnで試してください。

あなたは「手動で」すべてを呼び出しているので、thisポインタのインスタンスを渡す代わりに、nullを渡します。ですから、それは起こり得ますが、あなたは本当に一生懸命努力しなければなりません。

于 2012-05-16T19:31:08.580 に答える
5

thisnullは参照なので、型システムの観点からは問題ありません。

なぜNullReferenceException投げられなかったのかと尋ねるかもしれません。CLR がその例外をスローする状況の完全なリストは、文書化されています。あなたのケースはリストにありません。はい、それは ですcallvirt、 to Delegate.Invoke(こちらを参照) ではなく toBarであるため、this参照は実際には null 以外のデリゲートです!

ご覧の動作は、CLR の興味深い実装上の結果をもたらします。デリゲートには、非常に頻繁に、つまりデリゲートが静的である場合 (静的であると想像Targetしてください) であるプロパティ (参照に対応) があります。当然のことながら、 と呼ばれるプロパティのプライベート バッキング フィールドがあります。静的デリゲートの null が含まれていますか ? いいえ、そうではありません。 デリゲート自体への参照が含まれています。なぜヌルではないのですか?例が示すように null はデリゲートの正当なターゲットであり、CLR には静的デリゲートを区別するための 2 つのフレーバーのポインターがないためです。thisnullBar_target_targetnull

この些細なことは、デリゲートを使用すると、インスタンス メソッドの null ターゲットが後付けではないことを示しています。あなたはまだ究極の質問をしているかもしれません:しかし、なぜ彼らはサポートされなければならなかったのですか?

初期の CLR には野心的な計画がありました。とりわけ、宣誓 C++ 開発者にとっても最適なプラットフォームになるという野心的な計画がありました。この目標は、まずマネージド C++ で、次に C++/CLI でアプローチされました。難しすぎる言語機能のいくつかは省略されましたが、C++ では完全に正常なインスタンスなしで実行されるインスタンス メソッドをサポートすることについて、特に難しいことは何もありませんでした。デリゲート サポートを含む。

したがって、最終的な答えは次のとおりです。C# と CLR は 2 つの異なる世界だからです。

null インスタンスを許可する設計を示すためのより良い読みさらにより良い読みは、非常に自然な C# 構文コンテキストでもそのトレースを示します。

于 2012-05-16T19:33:20.807 に答える
0

これは C# クラスの読み取り専用参照です。したがって、予想どおり、これは他の参照と同じように使用できます(読み取り専用モードで)...

this == null // readonly - possible
this = new this() // write - not possible
于 2012-11-21T05:34:13.223 に答える