2011-11-08 28 views
11

代わりにparams引数を持つインスタンスメソッドを使用しているので、作成した拡張メソッドを呼び出すためにC#コンパイラを取得できません。paramsでインスタンスメソッドの代わりに拡張メソッドを強制的に使用する方法はありますか?

public class C 
{ 
    public void Trace(string format, params object[] args) 
    { 
     Console.WriteLine("Called instance method."); 
    } 
} 

と、拡張子::私のサンプルプログラムで

public static class CExtensions 
{ 
    public void Trace(this C @this, string category, string message, params Tuple<string, decimal>[] indicators) 
    { 
     Console.WriteLine("Called extension method."); 
    } 
} 

pubic void Main() 
{ 
    var c = new C(); 

    c.Trace("Message"); 
    c.Trace("Message: {0}", "foo"); 
    c.Trace("Category", "Message", new KeyValuePair<string, decimal>("key", 123)); 
} 

すべてのコールの印刷を例えば、私は次のクラスとそのメソッドを持って

を言いますCalled instance method.

明らかに、私はクラスCにアクセスできないのですが、私は拡張メソッドの作成に気を配りませんし、拡張機能も重要です。

コンパイラは、拡張メソッドよりもインスタンスメソッドを優先しますが、これが唯一のルールですか?つまり、Method(string format, params object[] args)のようなメソッドを持つクラスは、stringの最初のパラメータを持つ拡張メソッドを持つことはできません。

この動作の理由またはその回避方法(ではなく、 "単にCExtensions.Trace(c, "Category", ...と呼んでください)についての説明は、非常に高く評価されます。

+0

これまでに私はこれを見てきました。唯一の解決策は、メソッドの名前を変更することだと思われます。私は修正があるかどうかを知ることに興味があります。 – Polynomial

答えて

5

拡張機能を使用して既存のクラスメソッドを「引き継ぐ」ことはできません。

エクステンションなしでコールが機能する場合は、エクステンションを追加したときの動作は変更しないでください。この理由は、後で拡張機能を導入することで既存のコードが破損しないようにするためです。

別の名前を使用するか、クラスメソッドと異なるパラメータタイプを使用する必要があります。

+0

多くの人が回避策の良い提案をしてくれましたが(私にとってはうまくいきませんでしたが)、私の問題がどこから来ているかを完全に要約しているので、これを最善の答えと考えています。 – madd0

4

を直接入力することはできません。ターゲットインスタンスのメソッドは、拡張メソッドよりも常に優先されます。 これを行う方法(名前を同じに保ちながら)は、CExtensions.Traceアプローチを使用することです(質問の中で)。いくつかの場合において

、トリックここでC又は界面C器具のいくつかの基本クラスを使用することであろうが、をどのTrace方法を持っていない、および変数を再入力して、上の過負荷を追加しますあなたはstrと、最初の引数を保存するので、もしCExtensionsは、すなわち

IFoo foo = c; 
foo.Trace(...); 
+0

私は、ベースタイプに拡張メソッドを追加するトリックが好きです。もちろん、それは簡単すぎるでしょうし、私の場合はオブジェクトを拡張する必要があります。 – madd0

0

私は(TraceFormat?)あなたが最初のパラメータの型、または関数名を変更することができると思い

これは、バージョンをparamsは、非常に貪欲に見えますそれは常にすべての呼び出しをキャッチし、私が信じている拡張メソッドを無視します。

+0

メソッド名を変更したくないのは、開発者が慣れているからです。名前を変更すると、私たちが望むようにメソッドが使用されないことになります。最初の引数の型を 'string'以外のものに変更するのは難しいでしょう。なぜなら私は本当にカテゴリ名を' string'として欲しいからです。 – madd0

+0

実際にカテゴリとしての最初の引数は、たとえばEnumになるための良い候補になる可能性があり、作業を開始します。 –

+0

バレンタイン、素敵な試しですが、チームの中には、「カテゴリ」がたとえば顧客の名前である場合もあります。クライアントの基盤が成長し続けることを本当に望んでいるので、私はむしろ常に更新する必要のある列挙型を避けたいと思っています; – madd0

2

Tuple<string, decimal>は、KeyValuePair<string, decimal>と同じではありません。したがって、KeyValuePair<string, decimal>が渡されるのは、オブジェクトとしてとられ、したがって、params object[] argsのメンバメソッドが使用されます。

実際にKeyValuePairは、構造です。

+0

'Tuple'または' KeyValuePair'のように、実際にはあまり変更されていません。その両方の型(_any_型)は 'object'配列によって"吸収 "されます。 – madd0

+0

これは当てはまりますが、呼び出しで 'Tuple 'を使用すると、引き続き拡張メソッドは使用されません。 – Guffa

+0

はい、テストしただけで、使用していません。 – Aliostad

2

あなたが名前付き引数でそれを行うことができます。

c.Trace(
    "Category", 
    "Message", 
    indicators: new Tuple<string, decimal>("key", 123)); 

いますが、params機能を失うと、あなたが明示的に次のように、指標引数の配列を渡す必要があります:

c.Trace(
    "Category", 
    "Message", 
    indicators: new Tuple<string, decimal>[] 
    { 
     new Tuple<string, decimal>("key", 123), 
     new Tuple<string, decimal>("key2", 123) 
    }); 
+0

これはうまくいくが、この名前付き引数を忘れるのは簡単で、私たちは静かにこのことに気づかないでデフォルトのバージョンを呼び出すだろう。 –

+0

@Valentin Kuzubしかし、他のオプションもエラーが発生しやすいです。別の名前または異なる署名でメソッドを呼び出す必要があることを忘れるかもしれません。 –

+0

Joãoの議論は、メソッドの名前やシグネチャを変更したくない理由です。しかし、Valentinの権利:名前付き引数を使用してデフォルトのバージョンを呼び出すことを忘れてしまう可能性が非常に高いです(または、 )。 – madd0

関連する問題