3

多くの子クラスが継承する抽象クラスがあります。

  public abstract class CrawlerBase
    {
        public abstract void Process(string url);
    }

私はこのループに取り組んでいます:

foreach (var item in result)
                {
                    object crawler = null;

                    switch (item.Type)
                    {
                        case "Trials":
                            var t = new Trials(); 
                            ct.Process(item.URL); //repetitive code.  
                            break;

                        case "Coverage":
                            var c = new Coverage();
                            c.Process(item.URL); //repetitive code.
                            break;
                        default:
                            break;
                    }

                   // crawler.Process(item.URL);
                }

item.type 文字列は、どの子クラスをインスタンス化する必要があるかによって異なります。すべての子クラスが基本クラスを継承するため、すべての case ステートメントで .Process() を呼び出すのは非常に反復的です。コメントに示されているように、オブジェクト「クローラー」をインスタンス化される子クラスにキャストし、switch ステートメントの最後でクローラー.Process() を呼び出します。これどうやってするの?

4

5 に答える 5

4

これは機能しませんか?

foreach (var item in result)
{
    CrawlerBase crawler = null;

    switch (item.Type)
    {
        case "Trials":
            crawler = new Trials(); 
            break;
        case "Coverage":
            crawler = new Coverage();
            break;
        default:
            break;
    }

    if(crawler != null)
        crawler.Process(item.URL);
}
于 2012-06-07T18:58:04.307 に答える
2

ファクトリ メソッドで辞書を作成します。

var factories = new Dictionary<string, Func<CrawlerBase>>
{
  {"Trials", () => new Trials()},
  {"Coverage", () => new Coverage()},
// and so on
}

...そして捨てるswitch

foreach (var item in result)
{
  factories[item.Type]().Process(item.URL);
}
于 2012-06-07T18:59:02.480 に答える
2

文字列から型を作成する方法はたくさんあります。リフレクションはその 1 つです。遅いです (ただし、Web 要求を処理している場合は、おそらくこれを気にしないでしょう)。ただし、ハードコードされた文字列の長いリストを保持したり、い長い switch/case ステートメント。

速度が問題にならない場合は、簡単なことから始めましょう。

CrawlerBase crawler = (CrawlerBase)Activator.CreateInstance(
    Type.GetType("MyNamespace." + item.Type));

crawler.Process(item.URL);

リフレクションを使用すると、スイッチ/ケースは必要ありません。型の数に関係なく、ファクトリ コードを変更して の新しい実装に対応する必要はありませんCrawlerBase。新しい実装を追加するだけです。

使い方?Type定義から ( を使用して) クラスのインスタンスを作成できるActivator.CreateInstanceので、問題Type. クラスは、その (フル) 名からを作成するために使用できるType静的メソッドを提供します。この場合、名前空間を提供します。実際の名前空間に変更する必要があります。すべてのクラスを同じ名前空間に保持する場合は、次のように記述できます。GetTypeType

string rootNamespace = typeof(CrawlerBase).Namespace;

そして、メイクアップバージョン:

CrawlerBase crawler = (CrawlerBase)Activator.CreateInstance(
    Type.GetType(rootNamespace + "." + item.Type));

crawler.Process(item.URL);

改良点 1

これは非常に素朴な実装です。より良いバージョンTypeでは、辞書にキャッシュする必要があります。

private static Dictionary<string, Type> _knownTypes =
    new Dictionary<string, Type>();

private static GetType(string name)
{
    string fullName = typeof(CrawlerBase).Namespace
        + "." + name;

    if (_knownTypes.ContainsKey(fullName))
        return _knownTypes[fullName];

    Type type = Type.GetType(fullName);
    _knownTypes.Add(fullName, type);

    return type;
}

これで問題ありません。文字列の (おそらく) 長いリストを管理する必要はありません。タイプを追加/削除/変更したり、新しいクローラーを追加したりする場合は、無限のスイッチ/ケースを変更する必要はありません。やるべきことは、新しいクラスを派生させることです。この関数はスレッドセーフではないことに注意してください。複数のスレッドがある場合は、ロックを追加して辞書へのアクセスを保護する必要があります (または、ReadWriterLockSlim使用パターンによって異なります)。

改善点 2

次は何ですか?要件がより厳格な場合 (たとえば、セキュリティについて)、さらに何かを追加する必要がある場合があります (それはあなたの場合ではないので、型辞書は必要ないかもしれません)。もっとどうする?最初に、作成された型がから派生していることを確認できますCrawlerBase(IsAssignableFromより意味のある例外を提供するために、 の代わりに and を使用しInvalidCastExceptionます)。次に、クラスを属性で装飾します。その属性を持つクラスのみが作成されます (したがって、パブリックとしてマークされていても、ユーザーから作成できないプライベート実装を保持できます)。これ以外にもたくさんありますが、通常、この種の問題を解決するには単純な実装で十分です (入力がユーザーからではなく、堅牢な構成ファイルまたはプログラム自体から来る場合)。

于 2012-06-07T18:59:23.680 に答える
1

あなたはこれを行うことはできませんか?

 {
   CrawlerBase crawler = null;

   switch (item.Type)
   {
        case "Trials":
            crawler = new Trials();   
            break;

        case "Coverage":
            crawler = new Coverage();
            break;
        default:
            break;
    }
   crawler.Process(item.URL);

}

于 2012-06-07T18:59:06.490 に答える
1

Redi が指摘したように、すべてが同じ基本クラスから派生するため、どのインスタンスでも call Process を呼び出します。スイッチ (if/else) からマッピング コンストラクトに移動すると、単純化できる、switch ケースに反復的なコードがまだいくつかあります。

Dictionary<string, Type> Types = new Dictionary<string, Type>
{
    { "Trial", typeof(Trials) },
    { "Coverage", typeof(Coverage) },
};

void Crawl()
{
    string itemType = "Trial";
    CrawlerBase crawler = (CrawlerBase)Activator.CreateInstance(Types[itemType]);
    crawler.Process("url");
}

ファクトリ メソッドを値としてディクショナリに入れると、Dennis のようなデリゲートでも実行できます。

于 2012-06-07T19:06:06.600 に答える