Ковариация интерфейса C #, указать тип параметра - PullRequest
0 голосов
/ 24 мая 2019

Я пытался понять, как получить эквивалент следующего кода Java в C # (команда - это функциональный интерфейс).

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

Способ, которым мой код в настоящее время разрабатывается в Javaверсии, для типа C необходимо расширить Command, который, по моему пониманию, обрабатывается с ковариацией в C #.

Однако в соответствии с документацией C # что-то вроде следующего не будет работать, потому что "Тип используется только в качестве возвращаемого типа методов интерфейса и не используется в качестве типа аргументов метода"

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

Есть ли способ указать, что тип параметра для метода долженбыть ковариантным типу интерфейса в C #?

Я относительно новичок в C #, поэтому может быть, что это проблема XY, но я не нашел решения, которое бы работало до сих пор.

1 Ответ

2 голосов
/ 24 мая 2019

Я думаю, что вы ищете ограничение общего типа :

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

Это говорит о том, что T может быть чем угодно, лишь бы оно расширяло класс Command.

Ковариация в C # немного отличается от этого и касается преобразований между различными типами (массивами, обобщениями и делегатами).

Например, IEnumerable объявлен как IEnumerable<out T> { ... }, что делает его ковариантным. Это обещание компилятору, что вы будете когда-либо только брать элементы из из IEnumerable<T> и никогда не будете их вставлять.

Это означает, что писать безопасно, например,

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

Поскольку из IEnumerable можно извлекать только строки, можно смело делать вид, что они все объекты. Если вам разрешено помещать предметы в IEnumerable, то вы можете поместить любой старый объект в коллекцию, которая допускает только string, что было бы небезопасно.

Возьмем ваш пример, потому что вы только когда-либо добавляете Commands в IExecutor, вы можете объявить его как контравариантный:

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

Это позволит вам написать:

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

Это безопасно, потому что вы пообещали компилятору, что методы IExecutor будут принимать только Command объекты и никогда не будут их возвращать.

...