2017-05-01 30 views
3

私はC#のジェネリックスを理解するための簡単なコードを試しています。ここでのコードの目的は、自分の動物を持ち、さまざまなことをするように頼むトレーナー(例のために、ジャンプする)を持つことです。継承したクラスを継承する

問題は、トレーナーのコンストラクターにあります。私は犬、または猫を渡すことができるようにしたいと思います。彼らはどちらも同じクラスから継承しますが、型定義を指定したため、引数として渡すことはできないようですが、どちらも有効ではありません。 「動物」のようなジェネリッククラスを指定する方法があるので、犬や猫を渡してメンバーにしておくことができますか?

class AnimalDefinition 
{ 
    public Fur Fur; 
} 

class DogDefinition : AnimalDefinition 
{ 
    public BarkSound Bark; 
} 

class CatDefinition : AnimalDefinition 
{ 
    public MeowSound Meow; 
} 

class Animal<TDefinition> where TDefinition : AnimalDefinition 
{ 
    public TDefinition Definition; 

    public void Jump() 
    { 
     Console.WriteLine("Jump."); 
    } 
} 

class Dog : Animal<DogDefinition> 
{ 
    public Dog(DogDefinition def) 
    { 
     Definition = def; 
    } 
} 

class Cat : Animal<CatDefinition> 
{ 
    public Cat(CatDefinition def) 
    { 
     Definition = def; 
    } 
} 

class Trainer 
{ 
    Animal _animal; 

    public Trainer(Animal myAnimal) 
    { 
     _animal = myAnimal; 
    } 

    public MakeJump() 
    { 
     _animal.Jump(); 
    } 

    public Listen() 
    { 
     // if T is DogDefinition hear barking 
     // else if T is CatDefinition hear a meowing, etc 
    } 
} 

EDIT:Chris Bergerの回答(動作しますが、私は質問/回答論理を維持するためのコードを変更していません)に続く追加質問です。私はAnimalクラスに定義メンバーを追加しました。 Trainerクラスの中からBarkやMeowにアクセスする方法はありますか?または、TrainerクラスをCatTrainer : Trainer<CatDefinition>で派生させなければなりませんか?つまり、私たちはクラスで持っているものに似たものがある、

if(T is CatDefinition) 
{ // Meowing} 
else 
{} 
+0

public class Animal<T> where T : Animal<T> { } public class Dog : Animal<Dog> { } 

ここでは、そのパターンにもう少し読みます。 – SLaks

+1

「追加の」シナリオで何をしたいのかよくわからないし、2番目の質問をするために質問を編集するのは一般的にスタックオーバーフローの悪い考えだと思うが... 私はそれだと思う'if(T is CatDefinition){((Animal )_ myAnimal).Meow();のようなことをすることで可能です。 } ' これらのクラスの定義方法によります。私はそれがカプセル化の違反であると考えています。だから理想的にはそれを回避するより良い方法を見つけるでしょう。 –

答えて

2

私はあなたが、必ずしもこれにジェネリックを望んでいないことを、私は最初のコメンターに同意すると思いますが、あなたが望むのために他のいくつかの理由があると仮定するとジェネリック医薬品...

ここでの解決策は、Animal動物のクラスを作成することです。Animal <T>から派生しています。例えば

public class Animal 
{ 
    public virtual void Jump() 
    { 
     Console.WriteLine("Jump."); 
    }    
} 

public class Animal<T> : Animal where T : AnimalDefinition 
{ 
    public override void Jump() 
    { 
     //you can override Jump here if you want to 
    } 
} 

public class Dog : Animal<DogDefinition> {} 
public class Cat : Animal<CatDefinition> {} 

あるいは、実際に、二つ目のオプションは、一般的なパラメータにトレーナーの可視性を与えることです:

public class Animal<T> where T : AnimalDefinition 
{ 
    public void Jump() 
    { 
     Console.WriteLine("Jump."); 
    } 
} 

public class Dog : Animal<DogDefinition> {} 
public class Cat : Animal<CatDefinition> {} 

public class Trainer<T> where T : AnimalDefinition 
{ 
    Animal<T> _animal; 

    public Trainer(Animal<T> myAnimal) 
    { 
     _animal = myAnimal; 
    } 

    public MakeJump() 
    { 
     _animal.Jump(); 
    } 
} 

と接線として...これは良いかもしれません自己参照ジェネリックを使用する場所。あなたが実際にジェネリックが(代わりに、ちょうどctorのパラメータを渡す)したくないようですねhttps://blogs.msdn.microsoft.com/simonince/2008/06/12/generics-the-self-referencing-generics-pattern/

+0

ありがとう、それは完璧に動作します!追加情報をありがとう、私はこのパターンを知らなかった、シングルトンのために非常に便利です。 Trainerクラスの中でTの型を見つけることができるかどうかを調べるために付録を追加しましたか、閉じた構築クラスTrainer を継承するクラスを作る必要がありますか? – Yauda