7

次の例で、私が解決している問題について説明します。

class Animal {}
class Cat: Animal {}
class Dog : Animal { }

interface IAnimalHandler<in T> where T: Animal
{
    void Handle(T animal);
}

class AnimalHandler : 
    IAnimalHandler<Cat>,
    IAnimalHandler<Dog>
{
    public void Handle(Cat animal)
    {
        Console.Write("it's a cat !");
    }

    public void Handle(Dog animal)
    {
        Console.Write("it's a dog !");
    }
}

だから今、私はすべての動物を調べて、次のような適切なハンドラーを実行したいと思います:

  var ah = new AnimalHandler();
  var animals = new List<Animal> { new Cat(), new Dog() };
  animals.ForEach(a => ah.Handle(a));

ただし、.NETコンパイラがコンパイル前にここで使用されているタイプを知る必要があるという理由だけで、このコードは機能しません(メソッドHanler <> ...を解決できません)。この問題の最善の解決策は何でしょうか。言い換えると、実行時にタイプTのすべてのインスタンスに対してタイプTの適切なハンドラーを取得するように.NETコンパイラーに依頼する必要があります。ifインスタンスタイプをチェックする複数のステートメントを使用したくありません。

更新:それを見逃して申し訳ありませんが、それは私には明白に見えましたが、今ではそれほど明白ではないことを理解しています:AnimalHandlerクラスには、ドメインオブジェクトCatおよびDogの一部ではないロジックが含まれています。それらを純粋なプレーンドメインオブジェクトと考えてください。どのような種類のハンドラーについても知られたくないのです。

4

7 に答える 7

8

C#4dynamicを使用して、過負荷解決ステップをコンパイル時から実行時に移動できます。

var ah = new AnimalHandler();
var animals = new List<Animal> { new Cat(), new Dog() };
animals.ForEach(a => ah.Handle((dynamic)a));
于 2012-07-17T19:39:51.023 に答える
5

私には、このパターン(StructureMapを使用して実装)の恩恵を受けることができるように思えます。元のステートメントから、「実行時にタイプTのすべてのインスタンスに対してタイプTの適切なハンドラーを取得するように.NETコンパイラーに依頼する必要があります」とすると、次のようになります。

class Dog : Animal { }
class Cat : Animal { }

interface IHandler<T>
{
    void Handle(T eval);
}

class DogHandler : IHandler<Dog>
{
    public void Handle(Dog eval)
    {
        // do whatever
    }
}

class CatHandler : IHandler<Cat>
{
    public void Handle(Cat eval)
    {
        // do whatever
    }
}    

次に、リンクされた記事に従ってStructureMapを構成し、以下を使用して適切なハンドラーを取得できます。

var dogHandler = _container.GetInstance<IHandler<Dog>>(); // instance of DogHandler
var catHandler = _container.GetInstance<IHandler<Cat>>(); // instance of CatHandler

更新:これらをループで解決するには、次のようにします。

foreach (var animal in animals)
{
    var concreteHandlerType = typeof(IHandler<>).MakeGenericType(animal.GetType());
    var handler = _container.GetInstance(concreteHandlerType);
    handler.Handle(animal);
}

私はこのパターンをかなり大規模なシステムで使用して、同じ目標を達成します(純粋なドメインオブジェクト、それらのドメインオブジェクト内にあるべきではないロジックのハンドラー、簡素化されたメンテナンス)。これは、オブジェクトごとに個別のハンドラークラスが必要なシステムでうまく機能します。

于 2012-07-17T17:32:57.113 に答える
1

正確にあなたのコードですが、リフレクションを使用しています:

var ah = new AnimalHandler();
var animals = new List<Animal> { new Cat(), new Dog() };
animals.ForEach(a => {
  var method = ah.GetType().GetMethod("Handle", new Type[] {a.GetType()});
  method.Invoke(ah,new object[] { a });
});
于 2012-07-17T17:37:39.293 に答える
0

動物の種類ごとに特定のハンドラーがあるのはなぜですか。複数の特定のインターフェースを実装する代わりに、を実装するIAnimalHandler<T>だけで、単一のHandle(T obj)メソッドを使用できます。次にタイプ固有の機能が必要な場合は、を呼び出しtypeof(obj)て特定のタイプを取得することで処理できます。

于 2012-07-17T17:18:44.560 に答える
0

アプローチは次のとおりです。Animalで抽象メソッド(たとえば「BeingHandled()」と呼ばれるもの)を作成すると、Animalのすべての継承者が独自の実装を提供する必要があります。

次に、AnimalHandlerクラスには単一のHandle(Animal a)メソッドがあります。

class AnimalHandler
{
    public void Handle(Animal a)
    {
        a.BeingHandled();
    }
}

Animalから継承するものはすべて適切に実装されている必要があるため、Handle()に渡す動物は関係ありません。コンパイラは、Animal基本クラス内の抽象メソッド宣言によりこれを認識します。

于 2012-07-17T17:23:51.610 に答える
0

.NET 4.0を使用しているため、共分散/反変性を利用して、型のハンドラーを挿入します。

interface IAnimal
{
    string Name { get; set; }
}

class Dog : IAnimal
{
    public string Name { get; set; }
}

class Cat : IAnimal
{
    public string Name { get; set; }
}

interface IAnimalEvaluator<T>
{
    void Handle(IEnumerable<T> eval);
}

class AnimalHandler : IAnimalHandler<T> where T : IAnimal
{
    public void Handle(IEnumerable<T> eval)
    {
        foreach (var t in eval)
        {
            Console.WriteLine(t.Name);
        }
    }
}


List<Dog> dogs = new List<Dog>() { new Dog() { Name = "Bill Murray" } };
List<Cat> cats = new List<Cat>() { new Cat() { Name = "Walter Peck" } };

AnimalHandler <IAnimal> animalHandler = new AnimalHandler<IAnimal>();

animalEvaluator.Handle(dogs);
animalEvaluator.Handle(cats);
于 2012-07-17T17:25:14.637 に答える
0

ビジターパターンとダブルディスパッチを使用します。それはこのように動作します。ハンドラーはさまざまな種類の動物を処理できます。ハンドラーに正しい方法を選択させる代わりに、動物は正しい方法を選択します。動物は常に同じ方法(「彼の」方法)を必要とするので、これは簡単です。

class Animal
{
    string Name { get; set; }
    abstract public Handle(IAnimalHandler handler);
}

class Cat : Animal
{ 
    public overrides Handle(IAnimalHandler handler)
    {
        handler.Handle(this); // Chooses the right overload at compile time!
    }
}

class Dog : Animal
{ 
    public overrides Handle(IAnimalHandler handler)
    {
        handler.Handle(this); // Chooses the right overload at compile time!
    }
}

interface IAnimalHandler
{
    void Handle(Cat cat);
    void Handle(Dog dog);
}

class AnimalHandler : IAnimalHandler
{
    public void Handle(Cat cat)
    {
        Console.Write("it's cat {0}", cat.Name);
    }

    public void Handle(Dog dog)
    {
        Console.Write("it's dog {0}", dog.Name);
    }
}

今、あなたはこのような動物を扱うことができます

IAnimalHandler handler = new AnimalHandler();
animals.ForEach(a => a.Handle(handler));

handler = new SomeOtherAnimalHandler();
animals.ForEach(a => a.Handle(handler));
于 2012-07-17T17:37:26.813 に答える