web-dev-qa-db-ja.com

拡張クラス内から拡張メソッドを呼び出すために 'this'キーワードが必要なのはなぜですか

ASP.NET MVC ViewPageの拡張メソッドを作成しました。例:

public static class ViewExtensions
{
    public static string Method<T>(this ViewPage<T> page) where T : class
    {
        return "something";
    }
}

ビューからこのメソッドを呼び出すと(ViewPageから派生)、「CS0103:名前「メソッド」が現在のコンテキストに存在しません」というエラーが表示されますthisキーワードを使用して呼び出さない限り:

<%: Method() %> <!-- gives error CS0103 -->
<%: this.Method() %> <!-- works -->

thisキーワードが必要なのはなぜですか?それともそれがなくても機能しますが、何か不足していますか?

(この質問の重複があるに違いないと思いますが、私は見つけることができませんでした)

更新

Ben Robinsonによると のように、拡張メソッドを呼び出す構文はコンパイラシュガーです。では、なぜthisキーワードを必要とせずに、コンパイラが現在の型の基本型の拡張メソッドを自動的にチェックできないのでしょうか。

76
M4N

いくつかのポイント:

まず、提案されている機能(拡張メソッド呼び出しで暗黙の「this。」)は不要です。 LINQクエリ内包表記が希望どおりに機能するには、拡張メソッドが必要でした。受信者は常にクエリで指定されるため、LINQを機能させるために暗黙的なこれをサポートする必要はありません。

第二に、機能は動作しますagainst拡張メソッドのより一般的な設計:つまり、その拡張メソッド自分では拡張できないタイプを拡張することができますインターフェイスを使用していて、実装がわからない、または実装はわかっているがソースコードがないため。

タイプの拡張メソッドそのタイプ内を使用している場合は、ソースコードにdoでアクセスできます。 なぜ最初に拡張メソッドを使用しているのですか?拡張型のソースコードにアクセスできる場合は、インスタンスメソッドを自分で記述できます。拡張メソッドをまったく使用する必要はありません!実装では、オブジェクトのプライベート状態へのアクセスを利用できますが、拡張メソッドではできません。

アクセスできる型内から拡張メソッドを使いやすくすることは、インスタンスメソッドよりも拡張メソッドの使用を奨励しています。拡張メソッドはすばらしいですが、インスタンスメソッドがある場合は、通常、それを使用することをお勧めします。

これらの2つの点を考えると、機能がなぜnotが存在するのかを説明するという言語デザイナーの負担はなくなりました。なぜそれを説明するかはあなた次第ですすべき。機能には膨大なコストがかかります。この機能は必須ではなく、拡張メソッドの指定された設計目標に反して機能します。なぜそれを実装するコストを負担する必要があるのですか?この機能によってどのような説得力のある重要なシナリオが可能になるかを説明してください。今後実装することを検討します。それを正当化する説得力のある重要なシナリオは見当たらないが、おそらく私が見逃したものがある。

48
Eric Lippert

これがないと、コンパイラーは、ページを最初のパラメーターとして受け取る静的クラスの静的メソッドと見なします。つまり.

// without 'this'
string s = ViewExtensions.Method(page);

vs.

// with 'this'
string s = page.Method();
8
Ferruccio

インスタンスメソッドでは、「this」は暗黙的に各メソッドに透過的に渡されるため、提供するすべてのメンバーにアクセスできます。

拡張メソッドは静的です。 Method()またはthis.Method()ではなくMethod(this)を呼び出すことにより、コンパイラーにメソッドに何を渡すかを指示しません。

「呼び出し元のオブジェクトが何であるかを理解し、それをパラメーターとして渡すのはなぜですか?」

答えは、拡張メソッドは静的であり、「this」がない静的コンテキストから呼び出すことができるということです。

彼らはコンパイル中にそれをチェックすることができると思いますが、正直なところ、それはおそらく非常に少ないペイオフのために多くの仕事です。そして、正直に言うと、拡張メソッド呼び出しの明示性の一部を取り除くことにはほとんどメリットがありません。それらがインスタンスメソッドと間違われる可能性があるという事実は、それらが時々非常に直感的でない可能性があることを意味します(たとえば、NullReferenceExceptionsがスローされない)。拡張メソッドに新しい「パイプフォワード」スタイルの演算子を導入すべきだったと思うこともあります。

6
Alex Humphrey

次の点に注意することが重要です拡張メソッドと通常のメソッドには違いがあります。あなたはそれらの1つに遭遇したと思います。

別の違いの例を紹介します。nullオブジェクト参照で拡張メソッドを呼び出すのはかなり簡単です。幸い、これを通常の方法で行うのははるかに困難です。 (しかし、それは可能です。IIRC、ジョン・スキートは、CILコードを操作することによってこれを行う方法を示しました。)

static void ExtensionMethod(this object obj) { ... }

object nullObj = null;
nullObj.ExtensionMethod();  // will succeed without a NullReferenceException!

そうは言っても、拡張メソッドを呼び出すためにthisが必要であるというのは少し論理的でないように思えます。結局のところ、拡張メソッドは理想的には「感じ」、通常の方法と同じように動作する必要があります。

しかし実際には、拡張メソッドは、あらゆる点で言語にうまく適合する初期のコア機能よりも、既存の言語の上に追加された構文糖のようなものです。

3
stakx

これは、拡張メソッドがViewPageクラスに存在しないためです。拡張メソッドを呼び出す対象をコンパイラーに指示する必要があります。 this.Method()ViewExtensions.Method(this)の単なるコンパイラシュガーであることを忘れないでください。これは、クラスの途中でメソッド名を指定して拡張メソッドを呼び出すことはできません。

1
Ben Robinson

私は流暢なAPIに取り組んでおり、同じ問題に遭遇しました。拡張しているクラスにアクセスできても、Fluentメソッドのそれぞれのロジックを独自のファイルに含めたいと思っていました。 「this」キーワードは非常に直感的ではなく、ユーザーはメソッドが欠落していると考え続けました。私がやったことは、拡張メソッドを使用する代わりに、必要なメソッドを実装する部分クラスを自分のクラスにすることでした。回答にパーシャルの言及はありませんでした。この質問がある場合は、パーシャルがより良いオプションになるかもしれません。

0
Reyn