Generic method with Action parameter

2020-06-03 07:12发布

问题:

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

回答1:

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.



回答2:

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.



回答3:

Make the following changes:

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


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


回答4:

Try this.

Orangutan orangutan = new Orangutan();
Action<IAnimal> castedAction = action as Action<IAnimal>;
castedAction(orangutan);


回答5:

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