5

管理者がユーザーに表示されるさまざまなデータ入力フォームをカスタマイズできる .NET Web アプリケーションを作成しています。管理者が作成およびカスタマイズできる約 6 種類のフィールド タイプ (テキスト、数値、ドロップダウン、ファイル アップロードなど) があります。すべてのフィールドは、一連の基本属性/動作を共有します (フィールドは必須ですか? デフォルトのフィールド値はありますか?)。一連のフィールド固有の属性/動作もあります (つまり、ドロップダウンにはデータ ソース属性がありますが、テキスト フィールドにはありません)。簡単にするために、問題領域の他の多くの特徴を省略しています。

クラス階層は単純です。一般的な動作/属性をカプセル化する抽象的なスーパークラスと、フィールド固有のものを処理する約半ダースの具体的なサブクラスです。

各フィールド タイプは、特定のタイプの .NET サーバー コントロールとしてレンダリング (マップ) されます。これらはすべて System.Web.UI.Control から派生します。

フィールド ドメイン オブジェクトとそれに対応する UI コントロールの間で値をマッピングするために、次のコードを作成しました。

public static void Bind(Control control, IList<DocumentFieldBase> fieldBaseList)

     foreach (DocumentFieldBase fieldBase in fields){

            if (typeof (DocumentFieldText).IsInstanceOfType(fieldBase)){
                TextBox textbox = (TextBox) control;
                textbox.Text = (fieldBase as DocumentFieldText).GetValue();
            }

            if (typeof (DocumentFieldDropDown).IsInstanceOfType(fieldBase)){
                DropDown dropDown= (DropDown) control;
                dropDown.Text = (fieldBase as DocumentFieldSelectOne).GetValue().Text;
                dropDown.DataSource= (fieldBase as  DocumentFieldSelectOne).DataSource;
                dropDown.Id= (fieldBase as DocumentFieldSelectOne).GetValue().Id;
            }

            //more if statements left out for brevity
      }
}

型チェックを実行する無礼な if ステートメントを捨てたいと思います。私が目指していたアプローチは、サブクラスの型付けを使用して、フィールド/コントロールの組み合わせごとにメソッドのオーバーロードを作成することでした。例えば:

public static void Bind(TextBox control, DocumentFieldText fieldText){
 //some implementation code
}
public static void Bind(DropDown control, DocumentFieldDropDown fieldDropDown){
 //some implementation code
}  

次に、.NET に依存して、使用されている特定のサブクラスを使用して、実行時に適切なオーバーロードを呼び出すことができることを期待していました。たとえば、次のようになります。

foreach (DocumentFieldBase field in fields){
  Control control = FindControl(field.Identifier);
  Bind(control, field)
}

残念ながら、次のようにしようとすると、コンパイラがチョークします: Argument '1': cannot convert from 'System.Web.UI.Control' to 'TextBox'.

最初の引数を TextBox にキャストする必要がある場合は、自分で型チェックを実行することに戻り、この演習の目的全体を無効にします。

私が達成しようとしていることは、a) 可能で、b) 良いアイデアですか?

4

3 に答える 3

6

この質問の「ディスパッチ」タグは非常に適切です。必要なのは「複数のディスパッチ」と呼ばれるものです。C# (ほとんどの主流言語と同様) は「単一ディスパッチ」のみをサポートします。この場合、実行されるメソッドは、メソッドを呼び出すオブジェクトの (実行時) 型のみで選択され、引数の (実行時) 型では選択されません。

多くの場合、訪問者パターンを使用してこれを回避できます。アイデアは、実際の作業をDocumentFieldBase行うメソッド (具象サブクラスでもオーバーライドされる) を呼び出すメソッド(具体的なサブクラスでオーバーライドする) を提供することです。Control

残念ながら、Controlクラスのソース コードはおそらくあなたの管理下にはありません*...そのため、さらにハック的な解決策に頼る必要があります。この質問に対する受け入れられた回答は、リフレクションを使用するものを提供します。

*拡張メソッドは、静的メソッドの構文糖衣にすぎないため、コンパイル時に解決され、このシナリオでは役に立ちません。

于 2009-07-28T19:27:17.357 に答える
6

C# 4 より前では、すべてのオーバーロードはコンパイル時に行われます。実行時に効果的にオーバーロードするには、ダブルディスパッチまたはビジターパターンを使用する必要があり、それはすぐに面倒になります。

C# 4 では、変数を動的として宣言し、実行時にすべてを並べ替えることができました。

foreach (DocumentFieldBase field in fields){
  dynamic control = FindControl(field.Identifier);
  Bind(control, field)
}

明らかに、現時点ではあまり役に立ちません (VS2010b1 を使用している場合を除きます)。

1つのオプションは、からTypeへのマップを使用するAction<object>ことですが、継承の問題が発生します... (マップ内のエントリが見つかるまで、具象型からオブジェクトまでの型階層を作業し続ける必要がある可能性があります)。アクション内で正しいタイプにキャストする必要もあります:(

于 2009-07-28T19:19:33.177 に答える
0

DocumentFieldBase に抽象的で非静的なBind()メソッドを用意して、各具象クラスの実装内でダウンキャストを行うことはできませんか? 各DocumentFieldBaseクラスは、取得するタイプを知ってControlいますね。

于 2009-07-28T19:30:10.643 に答える