Почему не работает оператор С# с делегатами?

При ветвлении для выбора функции может иметь смысл использовать тернарный оператор для выбора функции, но это невозможно. Почему?

public class Demo {
    protected bool branch;
    protected void demo1 () {}
    protected void demo2 () {}
    public Action DoesntWork() {
        return branch ? demo1 : demo2;
    }
}

Компилятор выдает следующую ошибку:

Cannot implicitly convert type `method group' to `System.Action'

Ответ 1

Проблема заключается в том, что demo1 не является простым выражением, это метод. И методы могут быть переопределены, поэтому на самом деле это не один метод, это группа методов. Рассмотрим следующий пример:

public class Demo {
    protected bool branch;
    protected void demo1 (int) {}
    protected void demo1 () {}
    protected void demo2 () {}
    public Action DoesntWork() {
        return branch ? demo1 : demo2; //Error
        return demo1; //ok
    }
}

Теперь demo1 перегружен, поэтому какую из двух версий следует использовать? Ответ заключается в том, что перегруженная функция выбирается с использованием контекста, в котором используется функция.

В return demo1 очевидно, что он ожидает Action.

Но в return branch? demo1 : demo2; контекст не так прост. Тернарный оператор сначала пытается сопоставить тип demo1 с типом demo2, но это еще одна группа методов, поэтому там никакой помощи нет. Компилятор не смотрит дальше и не работает.

Решение состоит в том, чтобы очистить тип, ожидаемый от группы методов:

return branch? new Action(demo1) : demo2;

return branch? (Action)demo1 : demo2;

Action d1 = demo1;
return branch? d1 : demo2;

Ответ 2

Вы должны явно создать делегат соответствующего типа. Обычно вы можете просто использовать demo1 для ссылки на System.Action, но это только потому, что компилятор может вывести тип на основе использования и создать делегат для вас. В этом случае компилятор не знает, что ваш метод должен быть преобразован в System.Action при использовании в тернарном операторе.

Если вы предоставите это самостоятельно даже для одного из аргументов, он будет работать:

public Action DoesWork() 
{
    return branch ? demo1 : new Action(demo2);
}

Так как это явно выражает new Action для одного аргумента, компилятор может сделать вывод о том, что другой должен быть преобразован в соответствие с System.Action, и он будет скомпилирован успешно.