2013-04-22 6 views
6

私はasp.net mvcでUIコントロールを生成し、流暢なAPIを実現しようとするオブジェクト階層を持っています。私は、現在の問題に焦点を当てるためにいくつかのダミークラスを作成します。だからここ
は、「間違った」コードベースである:私はLinkBut​​tonコントロール上イドを呼び出そうとすると.NETで拡張メソッドを「オーバーライドする」方法

public abstract class HtmlElement { /* ... */ } 

public abstract class UIElement : HtmlElement { /* ... */ } 

public abstract class ButtonBase : UIElement { /* ... */ } 

public class LinkButton : ButtonBase { /* ... */ } 

public class ActionButton : ButtonBase { /* ... */ } 


public static class HtmlElementExtensions 
{ 
    public static T Id<T>(this T item, string id) where T : HtmlElement 
    { 
    /* set the id */ 
    return item; 
    } 
} 

public static class ButtonBaseExtensions 
{ 
    public static T Id<T>(this T item, string id) where T : ButtonBase 
    { 
    /* set the id and do some button specific stuff*/ 
    return item; 
    } 
} 

コンパイラはあいまいな呼び出しがあると言う:

LinkButton lb = new LinkButton().Id("asd"); 

私は本当にコンパイラが選択することを考えましたこのケースで最も近いのは、HtmlExtensions IdメソッドよりもHtmlElementから継承されたScriptクラスがあり、LinkBut​​ton(制限のため)のためにButtonBaseメソッドが呼び出されるということです。 私は解決策を持っていますが、より良い解決策があるかどうかはわかりません。
私はButtonBaseExtensionsからID方式を削除し、次のようにしてHtmlElementExtensions ID方式を変更:ButtonBaseからすべてのクラスの子孫が動作している。この方法を

public static T Id<T>(this T item, string id) where T : HtmlElement 
{ 
    if (item is ButtonBase) 
    { 
    /* do some button specific stuff*/ 
    } 
    /* set the id */ 
    return item; 
} 

。 HtmlElementロジックとButtonBaseロジックが混在するため、私のソリューションは実際には好きではありません。 より良い解決策をご提案/ご提案はありますか? 私はそれらを別の名前空間に入れたと思ったが、ちょっと待った。私は両方の名前空間を使用する必要がありますので、問題を解決しないでください。

msdnフォーラムでは、コンパイラがジェネリック拡張メソッドの制限事項を見なければならないと考えていると思いますか?私はいくつかのより多くの研究を行い、MSDNフォーラム上のスレッドを開始する一方

link
私はいくつかの非ジェネリック拡張methoddsを試してみました:

public class BaseClass { /*...*/ } 
    public class InheritedClass : BaseClass { /*...*/ } 

    public static class BaseClassExtensions 
    { 
    public static void SomeMethod(this BaseClass item, string someParameter) 
    { 
     Console.WriteLine(string.Format("BaseClassExtensions.SomeMethod called wtih parameter: {0}", someParameter)); 
    } 
    } 

    public static class InheritedClassExtensions 
    { 
    public static void SomeMethod(this InheritedClass item, string someParameter) 
    { 
     Console.WriteLine(string.Format("InheritedClassExtensions.SomeMethod called wtih parameter: {0}", someParameter)); 
    } 
    } 

そして、私はこれらをインスタンスの場合:

BaseClass bc = new BaseClass(); 
InheritedClass ic = new InheritedClass(); 
BaseClass ic_as_bc = new InheritedClass(); 

bc.SomeMethod("bc"); 
ic.SomeMethod("ic"); 
ic_as_bc.SomeMethod("ic_as_bc"); 

プロデュースされた出力:

BaseClassExtensions.SomeMethod called wtih parameter: bc 
InheritedClassExtensions.SomeMethod called wtih parameter: ic 
BaseClassExtensions.SomeMethod called wtih parameter: ic_as_bc 

You can vote it for now

おかげで、
ペーテル

+1

を**ない**仮想メソッドの代わりに、またそれが仮想行うことができます。これらのクラスを自分で宣言したので、なぜあなたが1つ必要なのかははっきりしていません。基本クラスの1つに仮想メソッドを追加し、必要に応じてそれを派生クラスでオーバーライドするだけです。 –

+0

流暢なAPIのため、私は拡張メソッドを使用する必要があります。ジェネリック継承は2レベルの継承でのみ機能します。 コンパイラがパラメータ制限を使用して呼び出す正しいメソッドを決定する非ジェネリック拡張メソッドの例を書きました。私は、コンパイラがジェネリックパラメータの制限を評価することを望みます。戻り値の型がButtonBaseであり、破損するため、public static T Id (このTアイテム、文字列ID)の代わりに "T:ButtonBase"ではなく "public static ButtonBase Id(このButtonBaseアイテム、文字列ID)流暢なAPIのメソッドチェーン。 –

答えて

2

あなたは、拡張メソッドについてのMSDNドキュメントの表情を取ることができます:Extension Methods (C# Programming Guide)。興味深い部分は、コンパイル時で拡張メソッドをバインド下にある:

...それが第一のタイプのインスタンスメソッドに一致するものを探します。一致するものが見つからない場合は、そのタイプに定義されている拡張メソッドを検索し、という最初の拡張メソッドにバインドします。

このように、この現象が発生します。実際には買うことができます。ただ1つのメソッドpublic static T Id<T>(this T item, string id) where T : objectでアプリケーションの動作を上書きすることができると想像してください。また、コンパイラのエラーが表示されない場合は、すべてが正しいと思われます。どのように混乱させることができますか?

あなたのアプローチについてもう一つ悪いことがあります。私があなたのAPIを消費する場合、私は2つの方法を持っています:HtmlElementExtensionsの1つとButtonBaseExtensionsの1つはButtonExtensions.Id(button, "id")ではなくHtmlElementExtensions.Id(button, "id")のことをやめますか?私は組み合わせたアプローチを好むだろう、あなたの場合は

:拡張メソッドがある

public static T Id<T>(this T item, string id) where T : HtmlElement 
{ 
    if (item is ButtonBase) 
    { 
     return (T)Id((ButtonBase)item); 
    } 
    else if (item is HtmlElement) 
    { 
     return (T)Id((HtmlElement)item); 
    } 

    throw new NotSupportedException("Type " + item.GetType() + " is not supported by Id extension method"); 
} 

private static ButtonBase Id(ButtonBase item, string id) 
{ 
    return item; 
} 

private static HtmlElement Id(HtmlElement item, string id) 
{ 
    return item; 
} 
+0

私の例では、継承ツリーではButtonBaseがオブジェクトよりLinkBut​​tonにはるかに近いため、ユーザーは自分のメソッドを上書きすることはできません。そして、あなたは本当に悪意のあるユーザーを想定します。私はそれを追加する方がはるかに簡単だと思う新しい例外()をスロー;エラーを出すコードに:D あなたの答えは私のソリューションと同じように見えますが、もう少しエレガントです。 –

+0

@Péteryapp、申し訳ありません。私は別の方向性について考えていた。もしあなたが 'ButtonBase'を持っていれば誰かが' LinkBut​​ton'のための拡張メソッドを書くでしょう。そして、はい、私は両方のソリューションを一度に意味する「複合アプローチ」によって) – outcoldman

関連する問題