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.

2
  • 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? Commented Oct 9, 2013 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
    Commented Oct 10, 2013 at 2:43

8 Answers 8

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.

12
  • 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
    Commented Oct 10, 2013 at 10:12
  • 1
    You can create a non-generic version of IAnimalGroomer; Then work with it in your list. Commented Oct 10, 2013 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
    Commented Oct 10, 2013 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
    Commented Oct 10, 2013 at 13:10
  • 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. Commented Oct 10, 2013 at 15:00
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.

5
  • Can you provide an example?
    – Kyle
    Commented Oct 9, 2013 at 22:23
  • i think the code doesn't compile public Interface IAnimalGroomer<T> : IAnimalGroomer where T:IAnimal{ Commented Oct 9, 2013 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. Commented Oct 9, 2013 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<T> public interface IAnimal{ string GetType(); } public interface IAnimalGroomer<T> : IAnimalGroomer where T : IAnimal { } Commented Oct 10, 2013 at 0: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<T> interface derive from that. Commented Oct 10, 2013 at 5: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.

1
  • 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
    Commented Oct 10, 2013 at 3: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!

5
  • 2
    Do I get a badge for "stealing" an accepted from Eric Lippert? :p
    – InBetween
    Commented Oct 10, 2013 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
    Commented Oct 10, 2013 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. Commented Oct 15, 2013 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
    Commented Oct 15, 2013 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<T> 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. Commented Oct 15, 2013 at 20:42
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

1
  • 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
    Commented Oct 9, 2013 at 22:10
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

3
  • 1
    This won't work since classes are invariant, and you can't use the IAnimalGroomer<T> interface instead since it isn't covariant in T and can't be made to be.
    – Lee
    Commented Oct 9, 2013 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
    Commented Oct 9, 2013 at 21:56
  • now in DogGroomer that totally defeats the purpose of using generics.
    – Eluvatar
    Commented Oct 9, 2013 at 22:33
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 = ...;
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();
}
2
  • 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
    Commented Oct 16, 2013 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. Commented Oct 16, 2013 at 19:46

Not the answer you're looking for? Browse other questions tagged or ask your own question.