简体   繁体   中英

C# interface covariance, specify parameter type

I've been trying to work out how to get an equivalent of the following Java code in C# (Command is a functional interface).

public interface Executor<C extends Command> {
    void execute(final C command) throws Exception;
}

The way my code is currently designed in the Java version, it is necessary for the type C to extend Command, which by my understanding is handled with covariance in C#.

However according to the C# docs, something like the following won't work because " The type is used only as a return type of interface methods and not used as a type of method arguments "

interface IExecutor<out Command>
{
    void Execute(Command command);
}

Is there a way of specifiying that the type of the parameter for a method must be the covariant to the type of the interface in C#?

I'm relatively new to C#, so it could be that this is an XY problem, but I haven't found a solution that would work so far.

I think what you're after is a generic type constraint :

interface IExecutor<T> where T : Command
{
    void Execute(T command);
}

This says that T can be anything, so long as it extends the Command class.

Covariance in C# is something a bit different to this, and it's about conversions between different types (arrays, generics, and delegates).

For example, IEnumerable is declared as IEnumerable<out T> { ... } , which makes it covariant. This is a promise to the compiler that you will only ever taken items out of the IEnumerable<T> , and never put them in.

This means that it's safe to write eg

IEnumerable<object> x = new List<string>();

Since you can only ever take strings out of the IEnumerable , it's safe to pretend they're all objects. If you were allowed to put items into the IEnumerable , then you could put any old object into a collection which only allows string , which would be unsafe.

To take your example, because you only ever put Commands into your IExecutor , you could declare it as contravariant:

interface IExecutor<in T> where T : Command
{
    void Execute(T command);
}

This would let you write:

IExecutor<Command> baseExecutor = ....;
IExecutor<SpecialisedCommand> executor = baseExecutor;

This is safe, because you've promised the compiler that the methods on IExecutor will only ever accept Command objects, and will never return them.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM