views:

420

answers:

5

So, I'm sure this has been answered somewhere out there before, but I couldn't find it anywhere. Hoping some generics guru can help.

    public interface IAnimal{}
    public class Orangutan:IAnimal{}

    public void ValidateUsing<T>(Action<T> action) where T : IAnimal
    {
        Orangutan orangutan = new Orangutan();
        action(orangutan);  //Compile error 1

        //This doesn't work either:
        IAnimal animal = new Orangutan();
        action(animal);  //Compile error 2
    }
  1. Argument type 'Orangutan' is not assignable to parameter type 'T'
  2. Argument type 'IAnimal' is not assignable to parameter type 'T'

Edit: Based on Yuriy and other's suggestions, I could do some casting such as:

    public void ValidateUsing<T>(Action<T> action) where T : IAnimal
    {
        Orangutan orangutan = new Orangutan();
        action((T)(IAnimal)orangutan);

        //This doesn't work either:
        IAnimal animal = new Orangutan();
        action((T)animal);
    }

The thing I wanted to do was call the ValidateUsing method like this:

ValidateUsing(Foo);

Unfortunately, if foo looks like this:

    private void Foo(Orangutan obj)
    {
        //Do something
    }

I have to explicitly specify the type when I call ValidateUsing

ValidateUsing<Orangutan>(Foo);
A: 

Try this.

    Orangutan orangutan = new Orangutan();
    Action<IAnimal> castedAction = action as Action<IAnimal>;
    castedAction(orangutan);
Stan R.
Thanks Stan! I'd upvote your answer if I had enough reputation.
Matt Hornsby
A: 

Make the following changes:

Orangutan orangutan = new Orangutan();
action((T)(IAnimal)orangutan); 


IAnimal animal = new Orangutan();
action((T)animal);
Yuriy Faktorovich
Hi Yuriy, this works very well. It's gets me part of the way (see edit). Thanks!
Matt Hornsby
+4  A: 

Why are you instantiating an Orangutan if you are supposed to be accepting any IAnimal?

public void ValidateUsing<T>(Action<T> action) where T : IAnimal, new()
{
    T animal = new T();
    action(animal);  //Compile error 2

If you reuse your generic parameter, you won't have any type issues...

Now, with regard to why your code doesn't work, all that you're saying is that the type T will derive from IAnimal. However, it could just as easily be a Giraffe as an Orangutan, so you can't just assign an Orangutan or IAnimal to a parameter of type T.

bdukes
+1 for explaining why instead of just rewriting the code.
Greg
Thanks bdukes, I just used Orangutan as an example. Probably a bad one. I want to be able to call the action with any IAnimal. In the "real" code, the IAnimal is stored as a private field in the class. So, I'm not really instantiating anything.
Matt Hornsby
A: 
    public interface IAnimal { }
    public class Orangutan : IAnimal { }

    public void ValidateUsing<T>(Action<T> action) where T : IAnimal
    {
        Orangutan orangutan = new Orangutan();
        action((T)(orangutan as IAnimal));  // needs to be cast as IAnimal

        //This doesn't work either:
        IAnimal animal = new Orangutan();
        action((T)animal);  // needs to be cast as T
    }

It also seems like the fact that it's an interface makes a difference. If you had an abstract class Animal, instead of an interface, you could do this :

    public abstract class Animal { }
    public class Orangutan : Animal { }

    public void ValidateUsing<T>(Action<T> action) where T : Animal
    {
        Orangutan orangutan = new Orangutan();
        action(orangutan as T); 

        //This doesn't work either:
        Animal animal = new Orangutan();
        action(animal as T); 
    }
climbage
Hi climbage, thanks for the detailed answer. I tend to use interfaces much more often than abstract classes (favor composition vs. inheritance and all that), but it's interesting to know that it works with an abstract class.
Matt Hornsby
+4  A: 

The thing is, that T represents some type which by the way implements IAnimal.

So, when you try to compile action(new Organatum()) you getting an error because you have declared that the action should take a parameter of type T which in its turn could be of type, let's say, Fish - you can't cast Organatum to a Fish, can you?

If you want to trigger any action which takes parameter of a type which implements IAnimal interface, then simply forget about generics and use Action<IAnimal>.

HTH.

archimed7592