2012-05-16 6 views
33

私はいつも、thisがインスタンスメソッド本体の中でnullになることは不可能だと考えました。シンプルなプログラムに従えば、それが可能であることを実証します。これは文書化された動作ですか?this == null .NETインスタンスメソッド - なぜ可能ですか?

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

UPDATE

私はそれがこの方法を文書化されている方法だと言って答えに同意します。しかし、私はこの行動を本当に理解していません。特にC#の設計方法ではないからです。私たちがnullにメソッドを呼び出し 書かれたコードを持っていた誰かからレポートを(おそらくC#を使用して.NETグループ の1()それはまだその時点でのC#の名前が付けられていないと思った)得ていた

ポインタであるが、 は、メソッドがどのフィールドにもアクセスしなかったため(つまり、 「this」はnullだったが、メソッド内で何も使用されていなかったため)例外が発生した。その方法は、 は、この点を使用して の例外を投げた別のメソッドを呼び出し、頭の掻き傷が少し続いた。彼らが を見つけた後、彼らは私たちにそれについてのメモを送った。 ヌルインスタンスのメソッドを呼び出すことができるのは ビットであると思いました。 Peter Goldeは、パーフォーマンスの影響 が常にcallvirtを使用していたことを確認するためにいくつかのテストを行いましたが、それは十分に小さく、 に変更を加えました。

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

+0

CLRが意図的にこのように.NET 1.0で設計されていることを示す回答を参照してください。あなたが引用している記事は、コンパイラの最適化である別の(デリゲートではない)状況です。インスタンスの型を静的に決定できるときに 'callvirt'を直接呼び出しで置き換えます。 'callvirt'の間にCLRが' NullReferenceException'を投げなければならない理由は、 'this'リファレンスに基づくVMTルックアップの必要性です。参照を確認する意味的な欲求だけではありません。 –

+0

関連:http://stackoverflow.com/q/3143498/158779 –

+0

IMHO、残念なことに、メソッドが 'callvirt'ではなく' call'で呼び出されるようにするための標準的な方法(属性によるもの)はありません。それは値をカプセル化する型[例えば 'string']がデフォルト値の格納場所で動作できるメンバを持つことを許していました。 'if(someString.IsNullOrEmpty)'が 'if(String.IsNullOrEmpty(someString)) 'よりもはるかに洗練されたものになると言ってください。 – supercat

答えて

23

あなたはあなたがnullオブジェクトのインスタンスメソッドを呼び出しているfirstArgumentDelegate.CreateDelegate

nullを渡しているので。

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

firstArgumentがnull参照とメソッドがインスタンスメソッドである場合、 結果はデリゲート型タイプのと メソッドのシグネチャに依存:

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

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

12

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

はBCLもヌルチェックがすべての時間(C#のような)callvirtを使用していない言語のデバッグを支援するために、この==を明示的に含まれない

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

を試してみてください。この情報については、questionを参照してください。

たとえば、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); 
} 
5

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

"手動で"すべてを呼び出すので、thisポインタのインスタンスを渡す代わりにnullを渡しています。それは起こることがありますが、あなたは本当に本当に頑張ってください。

+0

本当に簡単な方法については、次の例を参照してください。 http://bradwilson.typepad.com/blog/2008/01/c-30-extension.html –

+0

拡張メソッドはクラスメソッドのように見えるかもしれませんが、実際はそうではありません。それらは静的なメソッドで、クラスインスタンスに作用するもので、使い易さの点で便利です。だから私はそれを同じこととは言わない。 –

+0

私はあなたに同意しますが、リストは拡張メソッドで停止しません。 C++/CLIは非仮想C#メソッドを直接呼び出すので、拡張メソッドの例とまったく同じ滑らかなソースコードが得られます。今回はC#側で本物の 'this == null 'という結果になりました。 –

5

thisは参考になりますので、null型システムの観点からは問題ありません。

NullReferenceExceptionがスローされなかった理由を尋ねることがあります。 CLRが例外をスローする状況の完全なリストはdocumentedです。あなたのケースは記載されていません。はい、Barではなく、callvirtですが、Delegate.Invokesee here)であるため、thisの参照は実際にはnullではない代理人です!

表示される動作は、CLRにとって興味深い実装上の影響をもたらします。デリゲートがnullという非常に頻繁なTargetプロパティ(thisの参照に相当)を持っています。つまり、デリゲートが静的である場合です(想像するとBarは静的です)。当然、_targetと呼ばれる不動産のプライベートバッキングフィールドがあります。 _targetには静的デリゲートのためのnullが含まれていますか? No it doesn't.デリゲート自体への参照が含まれています。なぜnullではないのですか?あなたの例が示すようにnullがデリゲートの正当なターゲットであるため、CLRには何らかの理由で静的な代理人を区別するnullポインタの2つの味がありません。

このトリビュートのビットは、デリゲートでは、インスタンスメソッドのヌルターゲットが後から考慮されないことを示しています。あなたはまだ究極の質問をしているかもしれませんが、なぜ彼らをサポートしなければならないのですか?

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

したがって、最終的な答えは:C#とCLRは2つの異なる世界であるためです。

More good readingおよびeven better reading nullインスタンスを許可するデザインが非常に自然なC#構文的コンテキストでもそのトレースを示すことを示しています。

0

これはC#クラスの読み取り専用リファレンスです。したがって、これは他の参照(読み取り専用モード)のように使用できます。

this == null // readonly - possible 
this = new this() // write - not possible