22

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.

Ian R. O'Brien
  • 6,682
  • 9
  • 45
  • 73
Kyle
  • 3,775
  • 6
  • 37
  • 47
  • GetType() seems a bit misleading since it's not really returning a type. Why not make a public read only property for the class inheriting iAnimal? – Parrish Husband Oct 09 '13 at 22:46
  • Yea, GetType() is a mistake. I should have named it GetAnimalType(). As I mentioned, I come from the java world and forgot that GetType() is a method on Object class in c# – Kyle Oct 10 '13 at 02:43

8 Answers8

20

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.

Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Are you sure that the OP is asking for call site covariance? From what I gather from his question, he just wants to be able to put a DogGroomer and a CatGroomer in the same List . That is certainly possible in C#, as you mentioned in your answer. – Boluc Papuccuoglu Oct 09 '13 at 23:29
  • No, because DogGroomer and CatGroomer can't be upcast to the same base generic type. You can't upcast IAnimalGroomer to IAnimalGroomer, unles IAnimalGroomer is covariant which it is not. You could put them in a List, which isn't very useful. – svinja Oct 10 '13 at 01:22
  • After doing some research, this answer makes sense to me. I understand that c# is trying to prevent runtime errors (of accidentally passing a cat to a dog groomer). However, it comes at the expense of flexibility. I would really like to have a list of AnimalGroomers where I could figure out (at runttime) which one I want to use. – Kyle Oct 10 '13 at 04:24
  • 4
    @Kyle: If you are going to figure out at runtime which one you want to use then maybe you shouldn't be considering *generics* to begin with; your code seems to require *specific* types, not *generic*. Runtime decisions based on types doesn't mix well with *generics*; it defeats the purpose if you ask me. – InBetween Oct 10 '13 at 10:12
  • But there is a very good reason. It forces implementations of IAnimalGroomer to be tied to a specific type of animal. This is useful from an API point of view. Plus it eliminates the need for casting inside concrete IAnimalGroomer classes. My ultimate goal is to make an AnimalGroomerClinic that 'employs' different types of IAnimalGroomers. Then the AnimalGroomerClinic would have a method 'void Groom(IAnimal)' that then decides at runtime which type of IAnimalGroomer to use based on the animal passed in. – Kyle Oct 10 '13 at 12:24
  • 1
    You can create a non-generic version of IAnimalGroomer; Then work with it in your list. – Luis Filipe Oct 10 '13 at 13:01
  • 2
    *"But there is a very good reason. It forces implementations of IAnimalGroomer to be tied to a specific type of animal."*. Why do that via *generics*? Just implement a non-generic `IAnimalGroom` interface with `void Groom(IAnimal animal);` and you've tied it just the same. I think your scenario is too "type-coupled" for *genercis* to be any use, at least in C# where the type system is rightly working against you instead of helping. IMHO you are better off with non generic interfaces. – InBetween Oct 10 '13 at 13:03
  • 1
    To add to InBetween's answer, you can also have a method in your interface, `bool canGroomAnimal(IAnimal)`. The ugly part of all of this is that checking which groomer to use requires checking against every groomer to find one that is valid. But your approach has the same requirement. There are ways to avoid this performance cost, but it probably won't be a bottleneck (and solutions to that issue are beyond the scope of this question). – Brian Oct 10 '13 at 13:10
  • @InBetween: I realize I can solve the problem without generics. The reason I'd want to tie concrete implementations of IAnimalGroomer to a specific kind of IAnimal (at compile time) is to avoid all the casting I will have to do inside implementations of groomers for the usual benefits (readability, compile-time checking, etc). But alas, I think your are right...The c# type system is working against me. This would be a normal idiom to follow in the java world when you want to build a 'plugin' sort of system. – Kyle Oct 10 '13 at 13:27
  • @Kyle: The cast you are asking for is inherently unsafe; the C# type system is working against doing unsafe things. If you insist on taking the unsafe approach (which you pay for by turning compile errors into runtime exceptions), C# arrays do support [unsafe covariance](http://blogs.msdn.com/b/ericlippert/archive/2007/10/17/covariance-and-contravariance-in-c-part-two-array-covariance.aspx). Alteratively, you could probably (ab)use dynamic...but that's even worse. – Brian Oct 10 '13 at 14:56
  • 1
    @Brian: Well I'm not so sure that dynamic is actually worse here. Fundamentally the intention of the original poster here is some form of dynamic dispatch. If each animal knew how to groom itself then that would be single virtual dispatch. If there were different interactions between groomers and animals then that would be double virtual dispatch. I think the OP wants some kind of predicate dispatch. Dynamic dispatch might be the kind of dispatch that C# supports that is closest to the requirement. – Eric Lippert Oct 10 '13 at 15:00
  • @Brian: I think `dynamic` is a darn good idea in this scenario. Check out my answer below. You gave me the idea so credits go to you :p – InBetween Oct 10 '13 at 17:27
5

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.

Boluc Papuccuoglu
  • 2,318
  • 1
  • 14
  • 24
  • Can you provide an example? – Kyle Oct 09 '13 at 22:23
  • i think the code doesn't compile public Interface IAnimalGroomer : IAnimalGroomer where T:IAnimal{ – Stay Foolish Oct 09 '13 at 22:59
  • @StayFoolish Changed `Interface` to `interface` , it compiles now. Was just typing off the top of my head, not using an IDE :-( BTW, if you are referring to the curly brace at the end, it's there because the OP's original line ends with it. – Boluc Papuccuoglu Oct 09 '13 at 23:13
  • Are you sure the following code snippet is compiling, i use vs2012, it is not compiling. It is saying Incorrect number of type parameters in reference to interface IAnimalGrommer public interface IAnimal{ string GetType(); } public interface IAnimalGroomer : IAnimalGroomer where T : IAnimal { } – Stay Foolish Oct 10 '13 at 00:03
  • 1
    It does compile, are you sure you also defined interface IAnimalGroomer with: `public interface IAnimalGroomer{}` ? Because if you missed that, then the compiler will throw an error like you said. I just edited my answer to reflect that you have to define a **non-generic IAnimalGroomer interface and make the generic IAnimalGroomer interface derive from that.** – Boluc Papuccuoglu Oct 10 '13 at 05:59
4

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.

svinja
  • 5,495
  • 5
  • 25
  • 43
  • I found your response very enlightening. However, I didn't accept it , since I'm still at a loss on what to do here. Is my only option to do away with generics all together? So that my IAnimalGroomer interface has a method 'groom(IAnimal animal)'. Then implementers have to cast IAnimal in the groom() method/? – Kyle Oct 10 '13 at 03:14
3

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!

Community
  • 1
  • 1
InBetween
  • 32,319
  • 3
  • 50
  • 90
  • 2
    Do I get a badge for "stealing" an accepted from Eric Lippert? :p – InBetween Oct 10 '13 at 17:28
  • 1
    One thing I really like about this solution is that all of the dynamic dispatching is localized to the `AnimalClinic`. I usually worry that `dynamic` will increase the likelihood of introducing runtime exceptions, but here the risk seems very low. – Brian Oct 10 '13 at 18:45
  • 3
    I disagree with this approach. Using dynamic for conveniently ignoring the type system is not a good programming practice. There are several answers that define a non-generic interface that meet the requirement without resorting to dynamic. Personally, I would rank the above as an anti-pattern. – Andrew Hanlon Oct 15 '13 at 14:51
  • @Andrew Hanlon: I disagree, I am conveniently using *dynamic* to obtain a *dynamic* dispatch which is what the user wants. In the process I am not loosing the tightness the generic interface provides. What danger is there in ignoring the type system in this particular code? How can a consumer of the API generate a runtime exception due to a type violation? – InBetween Oct 15 '13 at 17:38
  • If the `AnimalClinic` class was the only public API, then I agree it doesn't really matter, it's a one off usage that doesn't pose issues. But someone else trying to implement a collection of the `AnimalGroomer` class would run into the same problem. If a non-generic base interface was used, this would not be an issue in the first place. I'll give you that it is a handy solution if the base model was not under your control, but I would never willfully implement it like this. – Andrew Hanlon Oct 15 '13 at 20:42
2

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

Stay Foolish
  • 3,636
  • 3
  • 26
  • 30
  • 1
    This won't work since classes are invariant, and you can't use the `IAnimalGroomer` interface instead since it isn't covariant in `T` and can't be made to be. – Lee Oct 09 '13 at 21:56
  • Sorry for being unclear...There is not supposed to be 'AnimalGroomer' in the 'java equivalent' code snippit. It's supposed to be IAnimalGroomer – Kyle Oct 09 '13 at 21:56
  • now in `DogGroomer` that totally defeats the purpose of using generics. – Eluvatar Oct 09 '13 at 22:33
2

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

Glenn Ferrie
  • 10,290
  • 3
  • 42
  • 73
  • I see that this would work for my situation, but it just seems like quite a bit of work, and sort of restrictive. I mean, what if I don't want my DogGroomer to have to inherit from an abstract class? It restricts the object hierarchy that I can use. – Kyle Oct 09 '13 at 22:10
1

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();
}
Andrew Hanlon
  • 7,271
  • 4
  • 33
  • 53
  • This solves the problem without using the dynamic keyword, but has the downside of forcing the implementer of IGroomer to cast their animal. What do you gain by using generics on the IGroomer class in this example? – Kyle Oct 16 '13 at 17:43
  • That's a good point. I have edited the solution to show a better example of how the generic class can be used to provide more functionality to inheritors, without the casting by implementing the interface explicitly and passing the value through to a virtual method. – Andrew Hanlon Oct 16 '13 at 19:46
1

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 = ...;
Jorn Vernee
  • 31,735
  • 4
  • 76
  • 93