2012-03-09 12 views
1

私は現在、C#で約400の数学関数を含んでおり、それぞれが独自の整数IDを持つアプリケーションを書いています。関数コードにアクセスするために、私のアプリケーションは現在、関数ごとに1つの 'case'ブロックを持つ巨大な 'switch'ブロックに関数のIDを供給しています。これは、シームレスかつ非常に高速に動作すると思われますが、コードブロックを関数識別子に照合するより効率的な方法はありますか?非常に大きな「switch」ブロックを使用して400ブロックのプログラムコードにアクセスすることを避けることはできますか?

+0

ノンリニアルックアップの辞書はどうですか? –

+2

これは保守性の悪夢のように聞こえる! – jason

+2

私は、そのIDで関数を参照する他の部分について興味があります。どのようなコードで 'tanh'関数を' 183'と呼ぶのはなぜですか? –

答えて

2

もっと良い解決策は、各機能をそれ自身のクラスにカプセル化し、次にstrategy patternを使用して必要な機能を交換することです。

それぞれの関数を独自のクラスにまとめると、その関数のコードを1か所で見るのが簡単になり、各クラスがその関数を実装するコードを担当する良いseparation of concernsを維持するという利点があります。これらの機能のテストも簡単になります。

これはまた、再コンパイルすることなく外部ソースから関数を追加する機会があるので、将来的に関数を追加する方が柔軟性があることを意味します。

このアプローチのもう1つの利点は、関数がすべてインターフェイスを実装するため、ロード時にそれらの実装のアセンブリをスキャンして、余分な労力を要することなく利用可能な関数のリストに新しい関数を自動的に追加できることです。

+2

400+クラスは400 + case switch文よりも優れた設計となっているのはなぜですか?それは単にクラスを使用しているからですか?私には同じメンテナンス作業のようですが、別の問題のために1つの問題だけを交換しました。正直なところ、これは素朴なOOPの "デザイン"を私に(私は怒らせるわけではありませんが、 'class'キーワードは万能薬ではないと思います。)個人的に私は関数オブジェクトを渡しますが、必要なツールが既に存在するときにクラスの束を作成する利点はありません。 –

+0

各クラスのテストが容易になるため、ある関数に必要な変更が他の関数のノックを持つ可能性は低くなります(ファイル内の偶発的なキーストロークや検索や間違いなど) –

+0

私はまったく同意しません。私はそれがテストが簡単だとは思わない、私はそれがあなたのプログラムをより良くするか維持しやすくしない定型コードの多くだと思う。 1つのクラスと関数を含むファイルを開くことは、switchステートメントで 'case 'を調べるよりも簡単です。それは、大きなスイッチが良い解決策ではないと私は思っています。関数オブジェクトは、まさにこの種のユースケースのために存在します。 OOPの流行語を投げ捨てることは良いプログラムではありません。 –

5

あなたがDictionary<string, Func<double, double>>または似たような...

// Better, use an *enum* of function IDs... 
private static readonly Dictionary<int, Func<double, double>> Functions = 
    new Dictionary<int, Func<double, double>> 
{ 
    { 0, Math.Sqrt }, 
    { 100, x => x * 2 }, 
    ... 
}; 

注意をしたいように、これはMath.Sqrtを指し同じように、それはまた、独自のメソッドを参照することができるとサウンド - あなたは配置する必要はありませんコレクションイニシャライザ内のすべてのロジック。おそらく、単一の式だけではない関数をメソッドに分割することになります。

その後、次のようににそれを適用したい:私は一般的な目的であることを、ここで辞書を使用しました

public void Apply(int functionId, double value) 
{ 
    Func<double, double> function; 
    if (!Functions.TryGetValue(functionId, out function)) 
    { 
     // Throw an exception or whatever 
    } 
    return function(value); 
} 

注意を。関数IDが0 ... nの場合は、代わりに配列を使用できます。

最初のコメントで指摘したように、整数だけではなく関数IDの列挙型を使用するほうが賢明です。

+0

このアプローチを試してみて、現在の 'スイッチ'設定との時間比較を行います。 – jonA

+2

時間の比較は?誰も気にしない?これはおそらくボトルネックにはなり得ず、パフォーマンスの差はとにかく重要ではありません。コードは、マシンを動作させるためのものではなく、マシンにしたいことを人間に伝えるためのものです。あなたは簡単にそれを欲しがることができ、あなたはそれを維持することができます。 – jason

+0

普通の静的関数よりもこのようなパターンを使う理由はわかりません。ここの利点は何ですか? –

0

Dictionary,this fellowは、6つ以上の文字列の場合があり、デフォルトの場合にはDictionaryを生成するという結果になります。ですから、コンパイル時にその変換をスキップしてみませんか?

+0

便利なリンクです。ありがとう。オブジェクトコードの解析段階では、関数名参照(「cos」など)を整数IDに変換してから、そのIDを 'switch'ステートメントに渡しています。関数名を 'case'ステートメントに直接渡してこの変換をスキップしてみます。 – jonA

1

私が知っている限り、いくつかの項目を含むステートメントは、内部的にルックアップテーブルを使用するようにコンパイラによって既に最適化されています。 Dictionaryは最小にする必要があります。しかし、いつものように、両方のアプローチを測定して決定する必要があります。

+0

このセクションに時間比較を行います。 – jonA

0

あなたはこのようなあなたのコード(psudocodeを)書いているように聞こえる:

function doMath(a,b, functionID) { 

    switch (functionID) { 
     case 1: 
      // addition function 
      return a + b; 
     break; 
     case 2: 
      // subtraction function 
      return a - b; 
     break; 
    } 

} 

あなたはこのようにそれを書きたいとき:

function add(a,b) { 
    return a + b; 
} 

function subtract(a,b) { 
    return a - b; 
} 

か、私はあなたの要件を誤解していますか?

+0

はい、これは私が使っていた「スイッチ」設定の一種です。私はあなたの提案を実験し、それがより経済的であるかどうかを見ます。 – jonA

+0

時間経済学に関しては、おそらくVisual StudioでC#で開発しています。 Intellisenseを使用すると、入力時に関数名を自動補完することができます。ビッグタイムセーバー。あなたがこのような基本的な質問をしているのであれば、初心者のプログラマーであると仮定するのは間違いありませんか? –

+0

さて、初心者ではなく、独学で(間違いなく)危険です。私はLinuxでMonoDevelopを使用しています(Visual Studioの多くの機能がIntellisenseに相当します)。この質問は、C#によってトライコードに時間がかかるように、「スイッチ」ブロックを含むか、関数の名前を解析してから割り当てられたコードブロックを解析するかどうかにかかわらず、「基本的」なものではない可能性があります。私は数日以内にタイムテストを行い、ここに結果を残します。 – jonA

関連する問題