.NET generics: how to enable type T at runtime?

Let me explain to you in the following example which problem I am solving:

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 a cat !"); } public void Handle(Dog animal) { Console.Write("it a dog !"); } } 

So now I want to go through all the animals and run the appropriate handler as follows:

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

However, this code will not work (the Hanler <> method cannot be resolved) just because the .NET compiler needs to know what type is used here before compiling, so what might be the best solution for this problem? In other words, I need to ask the .NET compiler to take an appropriate type T handler for each instance of type T at runtime . I do not want to use several if that check the type of the instance.

UPDATE: Sorry for not having it, it seemed obvious to me, but now I understand that it's not so obvious: the AnimalHandler class contains logic that should not be part of the Cat and Dog domain objects. Think of them as pure objects of a regular domain, I do not want them to know about any handlers.

+6
source share
7 answers

You can use C # 4 dynamic to move the overload resolution step from compile time at runtime:

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

It seems to me that you could use this template (implemented using StructureMap). Based on your original statement: β€œI need to ask the .NET compiler to take an appropriate type T handler for each instance of type T at runtime,” it might look something like this:

 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 } } 

Then you can customize StructureMap according to the related article and get the appropriate handler using:

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

UPDATE: To allow them in a loop, you can do something like this:

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

I use this template on a fairly large system to achieve the same goals (clean domain objects, handlers for logic that should not be inside these domain objects, simplified maintenance). It works well on a system where you want to have a separate handler class for each object.

+5
source

Exactly your code, but using reflection:

 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 }); }); 
+1
source

Why do you have special handlers for each type of animal. Instead of implementing several specific interfaces, just implement the IAnimalHandler<T> and just use one Handle(T obj) method. If you need specific type functionality, you can handle it by calling typeof(obj) to get the specific type.

0
source

Here's the approach: Make an abstract method in Animal, for example, called "BeingHandled ()", and then all descendants of Animal must provide their own implementation.

Then your AnimalHandler class will have one Handle (Animal a) method:

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

It doesn’t matter which animal you pass Handle (), because everything that inherits from Animal must have the correct implementation to work, and the compiler will know about it due to the declaration of the abstract method in your base class Animal.

0
source

Since you are using .NET 4.0, use covariance / contravariance to implement a handler for your types.

 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); 
0
source

Use visitor pattern and double submit. It works like that. A handler can process various types of animals. Instead of letting the handler choose the right method, animals choose the right method. This is easy, as the animal always needs the same method (β€œits own” method).

 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 cat {0}", cat.Name); } public void Handle(Dog dog) { Console.Write("it dog {0}", dog.Name); } } 

Now you can process such animals

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

Source: https://habr.com/ru/post/920711/


All Articles