1

私のプログラムでは、ユーザーがオブジェクトをダブルクリックすると、switch ステートメントを見て、どのイベントが発生するかを確認するリストボックスがあります。リストが大きくなり始めると、オブジェクトのリストを 2 つの場所 (リストボックスに追加するリストと switch ステートメントで 1 回) で維持する必要を回避する方法があるかどうかに興味があります。方法はありますか? switch ステートメントのさまざまなケースをインデックス化/読み取り/保存してから、それらをオブジェクトとしてリストボックスに追加しますか?

例: (機能しません。単なる理論です)

Switch (n)
ForEach (Case c in Cases)
{
   arrayCases.Add(c);
}
listbox.Items.AddRange(arrayCases);

編集:

私が今持っている辞書の推奨事項に進みます:

public void SetDictionary()
    {
       //add entries to the dictionary
        dict["cat"] = new Action(Cat);
        dict["dog"] = new Action(Dog);

        //add each dictionary entry to the listbox.
        foreach (string key in dict.Keys)
        {
            listboxTest.Items.Add(key);
        }                            
    }

     //when an item in the listbox is double clicked
     private void listboxTest_DoubleClick(object sender, EventArgs e)
     {
         testrun(listboxCases.SelectedItem.ToString());             
     }

     public void testrun(string n)
     {
         //this is supposed to receive the item that was double clicked in the listbox, and run it's corresponding action as defined in the dictionary.
         var action = dict[n] as Action action();
     }

上記のコードはほとんど正しいと思いますが、それを理解していると思いますが、アクション行: var action = dict[n] as Action action();

「アクション」には「;」が必要であるというエラーが表示されます。ここでの私の論理は正確ですか?もしそうなら、なぜアクションコールが間違っているのですか?

4

5 に答える 5

4

Dictionary<string, Action>避ける方法です。Dictionary.KeysになりListBox.Itemsます。

switch(n) になる

var action = dict[n] as Action
action();
于 2013-01-09T19:06:18.220 に答える
3

操作を別のクラスに移動することをお勧めします。次のような操作の基本クラスを作成します。おそらくフォームを操作する必要があるため、フォームのフィールドを追加しました。必要に応じて、他のオブジェクトを渡すこともできます。

internal abstract class Operation
{
    protected readonly MyForm form = null;

    protected Operation(MyForm form)
    {
        this.form = form;
    }

    public abstract String DisplayName { get; }

    internal abstract void Execute();
}

次に、操作ごとに 1 つのクラスを派生させます。

internal sealed class DoThis : Operation
{
    internal DoThis(MyForm form) : base(form) { }

    public override String DisplayName
    {
        get { return "Do this!"; }
    }

    internal override void Execute()
    {
        // Code to do this. You can use this.form to interact with
        // your form from this operation.
    }
}

internal sealed class DoSomethingElse : Operation
{
    internal DoSomethingElse(MyForm form) : base(form) { }

    public override String DisplayName
    {
        get { return "Do something else!"; }
    }

    internal override void Execute()
    {
        // Code to do something else.
    }
}

これで、すべての操作をリスト ボックスに追加できます

this.lsitBox.Items.Add(new DoThis(this));
this.lsitBox.Items.Add(new DoSomethingElse(this));

表示メンバー プロパティを設定します。

this.listBox.DisplayMember = "DisplayName";

最後に、イベント ハンドラーで選択した操作を実行します。

((Operation)this.listBox.SelectedItem).Execute();

このパターンにより、すべての操作が明確に分離され、将​​来の拡張が簡単かつクリーンになります。たとえばCanExecute、操作が現在利用可能かどうかを確認する必要がある場合は、すべての操作にプロパティを追加できます。または、ローカリゼーションをサポートする必要がある場合は、現在の UI 言語で操作の名前を表示するためのロジックを簡単に追加できます。

簡単にサポートできるもう 1 つのシナリオは、ログ記録、セキュリティ チェック、パフォーマンス測定など、すべての操作に共通するコードがある場合です。

internal abstract class Operation
{
    protected readonly MyForm form = null;

    protected Operation(MyForm form)
    {
        this.form = form;
    }

    public abstract String DisplayName { get; }

    protected abstract void ExecuteCore();

    internal void Execute()
    {
        Logger.Log("Executing operation " + this.DisplayName);

        try
        {
            this.ExecuteCore();

            Logger.Log("Executing operation " + this.DisplayName + " succeeded.");
        }
        catch (Exception exception)
        {
            Logger.Log("Executing operation " + this.DisplayName + " failed.", exception);

            throw;
        }
    }
}

ExecuteCore()の代わりにオーバーライドする必要があることに注意してくださいExecute()

最後に、インターフェイスIOperationを代わりに使用するか、抽象基本クラスと組み合わせて使用​​することも役立つ場合があります。これにより、不便な場合があるため、すべての操作が同じ基本クラスから継承される必要がなくなります。しかし、これ以上オーバーエンジニアリングしないように、これを省略しました。

于 2013-01-09T19:27:20.980 に答える
2

はい、ラムダの辞書を作成することでこれを行う方法があります。

void Main()
{
  // set up your dictionary
  Dictionary<string,Action> myList = new Dictionary<string,Action> {
     { "one", () => { Console.WriteLine("One function"); } },
     { "two",  () => { Console.WriteLine("Two function"); }},
     { "three", () => { Console.WriteLine("Three function"); }}
  };

  // do a "switch" (that is invoke a function that corresponds to a name)
  myList["one"]();

  // loop the list of keys (that is get a list of all the names)
  foreach (string key in myList.Keys)
    Console.WriteLine(key);
}

このプログラムの出力:

One function
one
two
three

また、この "switch" にこのように動的に追加することもできます (これはクールで、従来の switch ステートメントでは実行できないことです)。

myList.Add("four",() => { Console.WriteLine("Four function is dynamic"); });
于 2013-01-09T19:02:35.653 に答える
2

*通常のコードcaseでは列挙できません。switch

代わりにできることはswitch、「アクション名」から「アクションハンドラー」へのマップに置き換えることです。これにより、このマップをアクション名リストボックスのリストに再利用できます。サンプルについては、ティラックの回答を参照してください。

*) 本当に好奇心旺盛な人は、 の選択肢を列挙できますswitch。C# コードは IL に変換され、コードで IL を読み取ることができます。したがって、メソッドの IL を取得し、IL のパーサーを記述 (または既存の C# のパーサーをswitch取得)し、メソッド内の実装を見つけて、すべてのケースを選択できます。ビルド時に C# ソースに直接アクセスすることもできますが、IL の解析よりも複雑です。

于 2013-01-09T19:03:26.730 に答える
1

あなたのスイッチのケースの数が大きく変わるように思えます。これが当てはまる場合は、switch ステートメント以外のメカニズムの使用を検討することをお勧めします。おそらく、Alexi Levenkov が提案するようなことをしてから、保存されたアクション名のリストを反復処理し、関連付けられたハンドラーを実行したいと思うでしょう。これにより、アクション名をアクション マップに追加してからスイッチに追加する必要がなくなります。

于 2013-01-09T19:06:59.110 に答える