C# generic inheritance, base class casting - c#

I wanted to make the following inheritance with included generics, but the final cast
a as A<XBase> always results in null, because the cast is not valid. Could anybody elaborate on why this cast would be invalid, as well as maybe a solution to this problem.
public class XBase {}
public interface A<T> where T : XBase
{
//Edited
void Method(T param);
}
public class Implementor : A<Implementor.ImplementorX >
{
public class ImplementorX : XBase {public int a;}
//Edited
void Method(ImplementorX param) {}
}
public class HelloWorld
{
public static void Main(string[] args)
{
var a = new Implementor();
var castRes = a as A<XBase>;
Console.WriteLine(castRes != null);
}
}
see live example https://rextester.com/BTNVT61833
EDITED: Added a method to interface A<T> bc else it could be solved with #DavidG's response

If you make an explicit cast:
var castRes = A<XBase>(a);
then you will see the following error:
Unable to cast object of type '' to type '`
Why? In my view, it is better to understand using real world example. I've renamed classes based on this explanation. There are comments which maps explanations to your classes in question.
Abstractions:
// XBase
public class Animal { }
// class ImplementorX : XBase {public int a;}
public class Bird : Animal
{
public string WingColor { get; set; }
}
// interface A<T> where T : XBase
public interface IHat<T> where T : Animal
{
void Hide(T param);
T Pull();
}
Concrete implementations:
// class Implementor : A<Implementor.ImplementorX >
public class Peacock : IHat<Bird>
{
// void Method(ImplementorX param) {}
void IHat<Bird>.Hide(Bird param)
{ }
public Bird Pull()
{ }
}
and how it can be called:
public static void Main(string[] args)
{
Peacock peacockHat = new Peacock();
IHat<Animal> animalHat = (IHat<Animal>) peacockHat; // runtime error 'Unable to cast
// object of type 'HelloWorld.Peacock' to type 'HelloWorld.IHat`1
// because
animalHat.Hide(new Dolphin()); // Hide a dolphin in a peacock hat?
}
So we cannot hide hat of Peacock from Dolphin. It is not okay. CLR prevents us from making inappropriate actions.
In short:
In short, imagine you have two animals such as Wolf and Sheep. And these classes implements IAnimal interface:
public interface IAnimal
{ }
public class Wolf: IAnimal
{ }
public class Sheep : IAnimal
{ }
So Sheep, Wolf classes implement the inherited interface IAnimal:
IAnimal
/ \
/ \
Sheep Wolf
And then these animals can be put in cage:
public class Cage<T> where T : IAnimal
{
public void Put(T animal)
{ }
}
Then you create a cage for Sheep. After that somebody wants to cast Sheep cage to IAnimal:
Cage<Sheep> sheepCage = new Cage<Sheep>();
sheepCage.Put(new Sheep());
Cage<IAnimal> animalCage = (Cage<Wolf>)sheepCage; // compile error
// if there were no error, then you will be able to do:
animalCage.Put(new Wolf()); // it is not good idea

Related

More complex extending generics in C#

I have two trees of classes with generics and I cannot figure out one thing, that I know how to solve in Java with <? extends ...>.
First AI tree:
abstract class AbstractAI<TCtrl> : MonoBehaviour where TCtrl: IRotableWeaponController
abstract class AbstractShipAI : AbstractAI<IShipController>
class ModularAI : AbstractShipAI
Some other classes that extend AbstractAI and use various modules
Modules tree:
abstract class AbstractModule<TAi, TCtrl> : AIModule where TAi: AbstractAI<TCtrl> where TCtrl: IRotableWeaponController
{
public virtual void Init(TAi parent)
}
class ShootModule: AbstractModule<AbstractAI<IRotableWeaponController>, IRotableWeaponController>
Some other modules that extend AbstractModule and can be used only with some AIs, that have required capabilities
Used controller interfaces tree:
interface IRotableWeaponController
interface IShipController: IRotableWeaponController
Other interfaces extending IRotableWeaponController...
The issue is:
class ModularAI : AbstractShipAI
{
public ShootModule shootModule;
protected override void Start()
{
shootModule.Init(this); // Error: Argument type 'ModularAI' is not assignable to parameter type 'AbstractAI<IRotableWeaponController>'
}
}
ModularAI is AbstractAI<IShipController>, IShipController is IRotableWeaponController, but I get an error.
In Java I would do:
abstract class AbstractModule<TAi, TCtrl> : AIModule where TAi: AbstractAI<? extends TCtrl> where TCtrl: IRotableWeaponController
I know it's kind of complex and there is probably a better way to design it (and I may eventually get to it one day) but I want to know, how to solve this issue.
Please, how can I make it work?
I made an easier example to illustrate your issue :
class Fruit { }
class Apple : Fruit { }
class FruitMaker<T> where T : Fruit { }
class Program
{
static void HandleFruit(FruitMaker<Fruit> fruitMaker) { }
static void Main()
{
FruitMaker<Apple> appelMaker = new FruitMaker<Apple>();
FruitMaker<Fruit> fruitMaker = appelMaker; //cannot convert from 'FruitMaker<Apple>' to 'FruitMaker<Fruit>'
HandleFruit(fruitMaker);
}
}
What you need is generics covariance, you need to pass FruitMaker<Apple> to a method that expects FruitMaker<Fruit>.
This is enabled with the out keyword, what you want is class FruitMaker<out T> where T : Fruit { }, but you can't do that because FruitMaker is not an interface. You can solve this as follows :
class Fruit { }
class Apple : Fruit { }
interface IFruitMaker<out T> where T : Fruit { }
class FruitMaker<T> : IFruitMaker<T> where T : Fruit { }
class Program
{
static void HandleFruit(IFruitMaker<Fruit> fruitMaker) { }
static void Main()
{
FruitMaker<Apple> appelMaker = new FruitMaker<Apple>();
IFruitMaker<Fruit> fruitMaker = appelMaker; //this is allowed thanks to the out keyword
HandleFruit(fruitMaker);
}
}
Notice that I introduced an interface just to be able to add the out keywork.
You can use Covariance by inserting an interface in place of the abstract class, appropriately marked for it:
interface IAbstractAI<out TCtrl> where TCtrl : IRotableWeaponController {
}
abstract class AbstractAI<TCtrl> : MonoBehaviour, IAbstractAI<TCtrl> where TCtrl : IRotableWeaponController {
}
abstract class AbstractModule<TAi, TCtrl> : AIModule where TAi : IAbstractAI<TCtrl> where TCtrl : IRotableWeaponController {
public virtual void Init(TAi parent) {
}
}
class ShootModule : AbstractModule<IAbstractAI<IRotableWeaponController>, IRotableWeaponController> {
}
class ModularAI : AbstractShipAI {
public ShootModule shootModule;
protected override void Start() {
shootModule.Init(this); // Error: Argument type 'ModularAI' is not assignable to parameter type 'AbstractAI<IRotableWeaponController>'
}
}

Can i call a method that takes IEnumerable<Baseclass<superclass>> with IEnumerable<Derivedclass<subclass>>

I have some code that's equivalent to this (though this is a simplification):
namespace AnimalHospital
{
public class Animal { }
public class Dog : Animal { }
public class Cat : Animal { }
public interface Vet<T> where T : Animal
{
void takeCareOf(T animal);
}
public abstract class SpecializedDogVet<T> : Vet<T> where T : Dog
{
public abstract void takeCareOf(T dog);
}
public abstract class SpecializedCatVet<T> : Vet<T> where T : Cat
{
public abstract void takeCareOf(T cat);
}
public class AnimalHospital
{
public IList<SpecializedCatVet<Cat>> CatVets = new List<SpecializedCatVet<Cat>>();
public IList<SpecializedDogVet<Dog>> DogVets = new List<SpecializedDogVet<Dog>>();
private void treatSickAnimal(IEnumerable<Vet<Animal>> vets, Animal patient)
{
foreach(var vet in vets)
{
vet.takeCareOf(patient);
}
}
public void treatSickCat(Cat cat)
{
treatSickAnimal(CatVets, cat);
}
public void treatSickDog(Dog dog)
{
treatSickAnimal(DogVets, dog);
}
}
}
I get an error, telling me that conversion from:
IList<SpecializedCatVet<Cat>> to IEnumerable<Vet<Animal>> is not possible. How can this be? Before this they were nonegeneric, and i had some other problems, as i could not override the vet interfaces takeCareOf method. I had expected that as IEnumerable of Animal can easily be instantiated with a list of Dog, the same would be the case with parsing a collection of generics as long as their type parameter is a derirative of the required type. This is not the case though, and I seem unable to figure out why, or how to do this properly.
Thanks for reading.
UPDATE: I'm Accepting JLRishe's answer, makes perfect sense. Thank you very much.
This is not allowed, because if it were allowed, you could something like this:
var cat = new Cat();
treatSickAnimal(DogVets, cat);
And essentially try to force dog vets to treat a cat.
You can remedy this by making your methods generic on the animal parameter:
private void treatSickAnimal<T>(IEnumerable<Vet<T>> vets, T patient) where T : Animal
{
foreach (var vet in vets)
{
vet.takeCareOf(patient);
}
}
This should allow your code to compile correctly and ensure that you don't try to force any dog vets to treat cats.
Side note note - unless you plan on having specialized types (subclasses) of Dog and Cat and vets that are specialized for those subclasses, you can simplify the definition of SpecializedDogVet and SpecializedCatVet to be like this:
public abstract class SpecializedDogVet : Vet<Dog>
{
public abstract void takeCareOf(Dog dog);
}
public abstract class SpecializedCatVet : Vet<Cat>
{
public abstract void takeCareOf(Cat cat);
}
You would then refer to the types like this:
public IList<SpecializedCatVet> CatVets = new List<SpecializedCatVet>();
public IList<SpecializedDogVet> DogVets = new List<SpecializedDogVet>();
You cant because your Vet<T> interface is currently invariant, notice you can make it contravariant by specifying that it is input parameter like so:
public interface Vet<in T> where T : Animal
{
void takeCareOf(T animal);
}
This is possible because everywhere T is used as input parameter.
In order your code to compile you can do what #JLRishe said, or you can change your logic and make T output parameter, but in order to do that you need to change your Vet implementation so everywhere T is used it needs to be in position of output just an example:
public interface Vet<out T> where T : Animal
{
T takeCareOf();
}
So in conclusion, you can pass base class to interfaces or delegates where they have type parameters defined as output and you can pass more derived classes when type parameters are defined as input.
This is actually AMAZING article for covariance and contravariance if I got you hooked you should definitely check it http://tomasp.net/blog/variance-explained.aspx/

Passing inherited class

I'm trying some simple code to understand the generics in C#. The purpose of the code here is to have a trainer that has her own animal and will ask it to do various stuff (for the sake of the example, to jump).
The problem is with the constructor of the trainer. I would like to be able to pass a Dog, or a Cat. They both inherit from the same class, but because I specified the type definition it seems I can't pass them as argument, they can't be both valid. Is there a way to specify a generic class like "Animal" so I could pass a dog or a cat and keep it as a member?
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: Additional question following Chris Berger's answer (which works, but I didn't change the code to keep the question/answer logical). I added a definition member in the Animal class. Is there any way I can access Bark or Meow from inside the Trainer class or will I have to derivate the class Trainer with CatTrainer : Trainer<CatDefinition>? That is, is there something similar to what we have with classes,
if(T is CatDefinition)
{ // Meowing}
else
{}
I think I agree with the first commenter, that you don't necessarily want generics for this, but assuming you have some other reason for wanting generics...
The solution here is to create a class Animal, which Animal<T> derives from.
For example:
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> {}
Or, actually, a second option is to give Trainer visibility to the generic parameter:
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();
}
}
And as a tangent... this might be a good place to use a self-referential generic.
public class Animal<T> where T : Animal<T> { }
public class Dog : Animal<Dog> { }
Here's a little more reading on that pattern: https://blogs.msdn.microsoft.com/simonince/2008/06/12/generics-the-self-referencing-generics-pattern/

Why can't I access methods specific to my child class?

In C#, I have a parent class with a public member. I want to derive the parent class, then derive the class of the public member, so as to create and access new methods, as follows...
public class Animal { }
public class Sheep : Animal {
public void makeALamb() { }
}
public class Farm
{
public Animal myAnimal;
}
public class SheepFarm : Farm {
public void SheepFarm() {
this.myAnimal = new Sheep();
this.myAnimal.makeALamb();
}
}
This code doesn't compile. "Animal does not contain a definition for makeALamb()". But what I want to do is the essence of polymorphism, no? What am I missing? I'm greatly looking forward to finding out.
Thanks in advance!
Because myAnimal is of type Animal. As such, it can only access members of Animal... and Animal doesn't have a method makeALamb.
The right hand side of the assignment here:
this.myAnimal = new Sheep();
..says what it is. The left hand side says what your code sees it as. The left hand side in your assignment is:
public Animal myAnimal;
// ^^^^^^
Now.. pretend that what you've tried is possible.. consider this:
this.myAnimal = new Snake();
this.myAnimal.makeALamb(); // what does it call here?
this.myAnimal = new Giraffe();
this.myAnimal.makeALamb(); // what here?
..what should happen when you call makeALamb on a Snake instance? ...
If I'm guessing correctly what you're intending to do, consider using generics:
public class Farm<TAnimal> where TAnimal : Animal
{
public TAnimal myAnimal;
}
public class SheepFarm : Farm<Sheep>
{
public void SheepFarm()
{
this.myAnimal = new Sheep();
this.myAnimal.makeALamb();
}
}
makeALamb() is a method of Sheep, not Animal. Sheep derives from Animal, not the other way around.
this.myAnimal is defined as an Animal. Animal does not have a makeALamb method. Since you put a Sheep in this.myAnimal, you could cast it:
((Sheep)this.myAnimal).makeALamb();
This effectively says: "Take this.myAnimal, treat is as a Sheep and call makeALamb() on it". If this.myAnimal is not really a Sheep at run-time, you'll get an exception.
The idea is that your Animal can be instantiated as Sheep, but it will handle only the members defined on Animal. It is as simple as: Sheep is an Animal, but Animal is not a Sheep, so the Animal can not makeALamb() :)
You have to cast your Animal instance to Sheep if you want to access makeALamb method.
public void SheepFarm()
{
this.myAnimal = new Sheep();
((Sheep)this.myAnimal).makeALamb();
}
Without a cast myAnimal is considered as type of Animal and therefore you can only access the members of Animal and the members of base class(es) of Animal (which there is no base class olf Animal in this case).
On the other hand, ofcourse this is not an elegant and secure way to access a class member.If you want to access some common members via a base class instance, then you should declare that members in your base class (maybe as abstract or virtual), and then implement (or override) them in derived classes.
you have not defined the Farm class well. Here is example:
public class Animal { }
public class Sheep : Animal {
public void makeALamb() { }
}
public class Goat: Animal {
public void makeAGoat() { }
}
public class Farm
{
public Goat myGoat;
public Sheep mySheep;
}
public class MyFarm : Farm {
public void MyFarm() {
this.mySheep= new Animal();
this.mySheep.makeALamb();
}
}
Every animal is not goat but every goat is an animal.
Since, you want to use polymorphism, change your code to:
public abstract class Animal
{
public abstract void makeChild();
}
public class Sheep : Animal
{
public override void makeChild()
{
Console.WriteLine("A lamb is born.");
}
}
public class Cow : Animal
{
public override void makeChild()
{
Console.WriteLine("A calf is born.");
}
}
public class Farm
{
public Animal myAnimal;
}
public class SheepFarm : Farm
{
public SheepFarm()
{
this.myAnimal = new Sheep();
this.myAnimal.makeChild();
}
}
public class CowFarm : Farm
{
public CowFarm()
{
this.myAnimal = new Cow();
this.myAnimal.makeChild();
}
}
and, use the following "Main()" method:
class Program
{
static void Main(string[] args)
{
Farm sf = new SheepFarm();
Farm cf = new CowFarm();
}
}
However, I should point to you that this is not a good use case scenario. A better and simpler use case of polymorphism would be to get rid of the Farm classes and use the following "Main()" method instead of the previous one:
class Program
{
static void Main(string[] args)
{
Animal[] animals = {
new Sheep(),
new Cow(),
new Sheep(),
new Cow(),
new Cow()
};
foreach (Animal animal in animals)
{
animal.makeChild();
}
}
}

C# Generics: wildcards

I'm new to the c# world, and I'm trying to wrap my head around generics. Here is my current problem:
public Interface IAnimal{
string getType();
}
public Interface IAnimalGroomer<T> where T:IAnimal{
void groom(T);
}
Now I want to have a dictionary that contains these animal groomers. How do I do that? In java, I could do something like this:
HashMap<String,IAnimalGroomer<?>> groomers = new HashMap<>();
Edit: Here is an example of what I'm trying to do:
public class Dog : IAnimal
{
public string GetType()
{
return "DOG";
}
public void ClipNails() { }
}
public class DogGroomer : IAnimalGroomer<Dog>
{
public void Groom(Dog dog)
{
dog.ClipNails();
}
}
public class Program
{
private List<IAnimalGroomer<IAnimal>> groomers = new List<IAnimalGroomer<IAnimal>>();
public void doSomething()
{
//THIS DOESN"T COMPILE!!!!
groomers.Add(new DogGroomer());
}
}
EDIT
I think my intentions were unclear in the original post. My ultimate goal is to make an AnimalGroomerClinic that employs different types of IAnimalGroomers. Then animal owners can drop off animals at the clinic, and the clinic can decide which groomer should take care of the animal:
public class AnimalGroomerClinic
{
public Dictionary<String, IAnimalGroomer> animalGroomers = new Dictionary<String,IAnimalGroomer>();
public void employGroomer(IAnimalGroomer groomer){
animalGroomers.add(groomer.getAnimalType(), groomer);
}
public void Groom(IAnimal animal){
animalGroomers[animal.getAnimalType()].Groom(animal);
}
}
I realize I could do this without using generics. But the generics allow me to write the IAnimalGroomer interface in such a way that it is tied (at compile time) to a specific instance of IAnimal. In addition, concrete classes of IAnimalGroomer don't need to cast their IAnimals all the time, since generics would force implementations to deal with one specific kind of animal. I have used this idiom before in Java, and I'm just wondering if there is a similar way to write it in C#.
Edit 2:
Lots of interesting discussion. I'm accepting an answer that pointed me to dynamic dispatching in the comments.
What you want is call site covariance, which is not a feature that C# supports. C# 4 and above support generic variance, but not call site variance.
However, that doesn't help you here. You want a dog groomer to be put in a list of animal groomers, but that can't work in C#. A dog groomer cannot be used in any context in which an animal groomer is needed because a dog groomer can only groom dogs but an animal groomer can also groom cats. That is, you want the interface to be covariant when it cannot be safely used in a covariant manner.
However your IAnimalGroomer<T> interface could be contravariant as it stands: an animal groomer can be used in a context in which a dog groomer is required, because an animal groomer can groom dogs. If you made IAnimalGroomer<T> contravariant by adding in to the declaration of T then you could put an IAnimalGroomer<IAnimal> into an IList<IAnimalGroomer<Dog>>.
For a more realistic example, think of IEnumerable<T> vs IComparer<T>. A sequence of dogs may be used as a sequence of animals; IEnumerable<T> is covariant. But a sequence of animals may not be used as a sequence of dogs; there could be a tiger in there.
By contrast, a comparer that compares animals may be used as a comparer of dogs; IComparer<T> is contravariant. But a comparer of dogs may not be used to compare animals; someone could try to compare two cats.
If that is still not clear then start by reading the FAQ:
http://blogs.msdn.com/b/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx
and then come back and ask more questions if you have them.
There are two interfaces, IEnumerable and IEnumerable<T> which are close to what you are trying to accomplish. So you can have a dictionary like Dictionary<string,IEnumerable> which can contain as values IEnumerable<int>, IEnumerable<string>, etc. The trick here is to derive IAnimalGroomer<T> from IAnimalGroomer, a non generic interface.
EDIT:
As an example, per your request, after creating an interface called IAnimalGroomer with:
public interface IAnimalGroomer{
}
, if you change the line that reads:
public interface IAnimalGroomer<T> where T:IAnimal{
to
public interface IAnimalGroomer<T> : IAnimalGroomer where T:IAnimal{
and the line that reads:
private List<IAnimalGroomer<IAnimal>> groomers = new List<IAnimalGroomer<IAnimal>>();
to
private List<IAnimalGroomer> groomers=new List<IAnimalGroomer>();
your code should compile and work.
I know this has been Lipperted but I still feel like answering. The List is a red herring here, it doesn't matter that you're using it.
The reason this doesn't work is because IAnimalGroomer<T> itself is not covariant, and it can't be made covariant explicitly because of the groom(T) method. It is illegal to cast IA<Derived> to IA<Base> in the general case, or in different words, generic interfaces are not covariant by default. The List<T>.Add method is what triggers a cast from DogGroomer (which is IAnimalGroomer<Dog>) to IAnimalGroomer<IAnimal>, but for example, this still won't work:
IAnimalGroomer<Dog> doggroomer = new DogGroomer(); // fine
IAnimalGroomer<IAnimal> animalgroomer = doggroomer; // invalid cast, you can explicitly cast it
// in which case it fails at run time
If this worked (so if IAnimalGroomer<T> was covariant), you could in fact also add a DogGroomer to your list, despite the List<T> not being covariant! That's why I said the list is a red herring.
The reason generic interface covariance isn't the default is because of type safety. I added Cat/CatGroomer classes to your code that are basically the same as the ones for dogs. Look at the main function and the comments in it.
public interface IAnimal
{
string getType();
}
public interface IAnimalGroomer<T> where T:IAnimal
{
void groom(T t);
}
public class Dog : IAnimal
{
public string getType() { return "DOG"; }
public void clipNails() { }
}
public class DogGroomer : IAnimalGroomer<Dog>
{
public void groom(Dog dog)
{
dog.clipNails();
}
}
public class Cat : IAnimal
{
public string getType() { return "CAT"; }
public void clipNails() { }
}
public class CatGroomer : IAnimalGroomer<Cat>
{
public void groom(Cat cat)
{
cat.clipNails();
}
}
public class Program
{
static void Main(string[] args)
{
// this is fine.
IAnimalGroomer<Dog> doggroomer = new DogGroomer();
// this is an invalid cast, but let's imagine we allow it!
IAnimalGroomer<IAnimal> animalgroomer = doggroomer;
// compile time, groom parameter must be IAnimal, so the following is legal, as Cat is IAnimal
// but at run time, the groom method the object has is groom(Dog dog) and we're passing a cat! we lost compile-time type-safety.
animalgroomer.groom(new Cat());
}
}
There are no sequences used, yet the code would still break type safety if it was legal.
This type of cast could be allowed, but the errors caused by it would happen at run-time, which I imagine was not desirable.
If you mark the type parameter T as "out", then you can cast A<Derived> into A<Base>. However, you can no longer have a method with T as an argument, which you do. But it eliminates the problem of trying to shove a Cat into a Dog.
IEnumerable<T> is an example of a covariant interface - it has no f(T) methods so the problem can't happen, unlike with your groom(T) method.
As Brian pointed out in comments above, maybe dynamic is the way to go here.
Check out the following code. You get the benefits of generics to tie down the API nicely and under the hoods you use dynamic to make things work.
public interface IAnimal
{
}
public class Dog : IAnimal
{
}
public class Cat : IAnimal
{
}
public class BigBadWolf : IAnimal
{
}
//I changed `IAnimalGroomer` to an abstract class so you don't have to implement the `AnimalType` property all the time.
public abstract class AnimalGroomer<T> where T:IAnimal
{
public Type AnimalType { get { return typeof(T); } }
public abstract void Groom(T animal);
}
public class CatGroomer : AnimalGroomer<Cat>
{
public override void Groom(Cat animal)
{
Console.WriteLine("{0} groomed by {1}", animal.GetType(), this.GetType());
}
}
public class DogGroomer : AnimalGroomer<Dog>
{
public override void Groom(Dog animal)
{
Console.WriteLine("{0} groomed by {1}", animal.GetType(), this.GetType());
}
}
public class AnimalClinic
{
private Dictionary<Type, dynamic> groomers = new Dictionary<Type, dynamic>();
public void EmployGroomer<T>(AnimalGroomer<T> groomer) where T:IAnimal
{
groomers.Add(groomer.AnimalType, groomer);
}
public void Groom(IAnimal animal)
{
dynamic groomer;
groomers.TryGetValue(animal.GetType(), out groomer);
if (groomer != null)
groomer.Groom((dynamic)animal);
else
Console.WriteLine("Sorry, no groomer available for your {0}", animal.GetType());
}
}
And now you can do:
var animalClinic = new AnimalClinic();
animalClinic.EmployGroomer(new DogGroomer());
animalClinic.EmployGroomer(new CatGroomer());
animalClinic.Groom(new Dog());
animalClinic.Groom(new Cat());
animalClinic.Groom(new BigBadWolf());
I'm not sure if this is somewhat what you were looking for. Hope it helps!
Here is some code that works. I've added some classes and switch AnimalGroomer to be an abstract class not an interface:
class Program
{
static void Main(string[] args)
{
var dict = new Dictionary<string, IGroomer>();
dict.Add("Dog", new DogGroomer());
// use it
IAnimal fido = new Dog();
IGroomer sample = dict["Dog"];
sample.Groom(fido);
Console.WriteLine("Done");
Console.ReadLine();
}
}
// actual implementation
public class Dog : IAnimal { }
public class DogGroomer : AnimalGroomer<Dog>
{
public override void Groom(Dog beast)
{
Console.WriteLine("Shave the beast");
}
}
public interface IAnimal {
}
public interface IGroomer
{
void Groom(object it);
}
public abstract class AnimalGroomer<T> : IGroomer where T : class, IAnimal
{
public abstract void Groom(T beast);
public void Groom(object it)
{
if (it is T)
{
this.Groom(it as T);
return;
}
throw new ArgumentException("The argument is not a " + typeof(T).GetType().Name);
}
}
Please let me know if there are any questions
From my understanding, you cannot put the type constraints in the parameter in this case. which means you might need to do the boxing and unboxing. you might need to use a normal interface.
public interface IAnimal{
string GetType();
}
public interface IAnimalGroomer{
void Groom(IAnimal dog);
}
public class Dog : IAnimal
{
public string GetType()
{
return "DOG";
}
public void ClipNails()
{
}
}
public class DogGroomer : IAnimalGroomer
{
public void Groom(IAnimal dog)
{
if (dog is Dog)
{
(dog as Dog).ClipNails();
}
else {
// something you want handle.
}
}
}
public class Program
{
private List<IAnimalGroomer> groomers = new List<IAnimalGroomer>();
public void doSomething()
{
groomers.Add(new DogGroomer());
}
}
Or maybe you need to have another technical design for solving your problem
I'm adversed to using dynamic, because it has a runtime cost to it.
One simpler solution, uses a Dictionary<string, object> in which you can safely store any IAnimalGroomer<T>.
public class AnimalGroomerClinic {
public Dictionary<string, object> animalGroomers = new Dictionary<string, object>();
public void employGroomer<T>(IAnimalGroomer<T> groomer) where T : IAnimal {
animalGroomers.Add(groomer.getAnimalType(), groomer);
}
public void Groom<T>(T animal) where T : IAnimal {
// Could also check here if the 'as' operator returned null,
// which might happen if you don't have the specific groomer
(animalGroomers[animal.getAnimalType()] as IAnimalGroomer<T>).groom(animal);
}
}
Now, this requires a cast, which you might say is unsafe. But you know it's safe due to encapsulation. If you put an IAnimalGroomer<Dog> into the hashmap under the key "dog". And request it again with the key "dog", you know it will still be an IAnimalGroomer<Dog>.
Just like with the java equivalent:
class AnimalGroomerClinic {
public Map<String, Object> animalGroomers = new HashMap<>();
public <T extends IAnimal> void employGroomer(IAnimalGroomer<T> groomer) {
animalGroomers.put(groomer.getAnimalType(), groomer);
}
#SuppressWarnings("unchecked")
public <T extends IAnimal> void Groom(T animal) {
((IAnimalGroomer<T>) animalGroomers.get(animal.getAnimalType())).groom(animal);
}
}
Which still requires an unchecked cast (even if you change Object to IAnimalGroomer<?>). The point is that you're trusting your encapsulation enough to do an unchecked cast.
It doesn't really add anything to have IAnimalGroomer<?> instead of Object in terms of type safety. Because you're encapsulation already ensures more.
It could be done for readability, to indicated what kind of objects the map holds by having IAnimalGroomer<T> implement a stub interface:
public interface IAnimalGroomerSuper {
// A stub interface
}
public interface IAnimalGroomer<T> : IAnimalGroomerSuper where T : IAnimal {...}
Then the dictionary could be:
public Dictionary<string, IAnimalGroomerSuper> animalGroomers = ...;
The point is to use a non-generic interface behind the scenes to limit the types, but only expose the generic version.
void Main()
{
var clinic = new AnimalClinic();
clinic.Add(new CatGroomer());
clinic.Add(new DogGroomer());
clinic.Add(new MeanDogGroomer());
clinic.Groom(new Cat()); //Purr
clinic.Groom(new Dog()); //Woof , Grrr!
}
public interface IAnimal {}
public interface IGroomer {}
public class Dog : IAnimal
{
public string Woof => "Woof";
public string Growl => "Grrr!";
}
public class Cat : IAnimal
{
public string Purr => "Purr";
}
public interface IGroomer<T> : IGroomer where T : IAnimal
{
void Groom(T animal);
}
public class DogGroomer : IGroomer<Dog>
{
public void Groom(Dog dog) => Console.WriteLine(dog.Woof);
}
public class MeanDogGroomer : IGroomer<Dog>
{
public void Groom(Dog dog) => Console.WriteLine(dog.Growl);
}
public class CatGroomer : IGroomer<Cat>
{
public void Groom(Cat cat) => Console.WriteLine(cat.Purr);
}
public class AnimalClinic
{
private TypedLookup<IGroomer> _groomers = new TypedLookup<IGroomer>();
public void Add<T>(IGroomer<T> groomer) where T : IAnimal
=> _groomers.Add<T>(groomer);
public void Groom<T>(T animal) where T : IAnimal
=> _groomers.OfType<T, IGroomer<T>>().ToList().ForEach(g => g.Groom(animal));
}
public class TypedLookup<T> : Dictionary<Type, IList<T>>
{
public void Add<TType>(T item)
{
IList<T> list;
if(TryGetValue(typeof(TType), out list))
list.Add(item);
else
this[typeof(TType)] = new List<T>{item};
}
public IEnumerable<TRet> OfType<TType, TRet>() => this[typeof(TType)].Cast<TRet>();
public TRet First<TType, TRet>() => this[typeof(TType)].Cast<TRet>().First();
}

Categories