Что делает имя метода эквивалентным делегату действия?

#c# #delegates

#c# #делегаты

Вопрос:

Я просто экспериментировал и в итоге получил следующий фрагмент:

 public static class Flow {
    public static void Sequence(params Action[] steps) {
        foreach (var step in steps)
            step();
    }
}
void Main() {
    Flow.Sequence(() => F1(), () => F2());
    Flow.Sequence(F1, F2); // <-- what makes this equiv to the line above?

}
void F1() { }
void F2() { }
  

Я не понимал, что одно только имя метода совпадает с действием.

Что делает это так?

Комментарии:

1. На самом деле они не эквивалентны; последнее лучше, потому что позволяет избежать дополнительного, пустого вызова метода. (которое, я допускаю, может быть оптимизировано, но все же; они не эквивалентны)

Ответ №1:

В C # делегаты — это не что иное, как указатели на методы. Они могут указывать на существующие методы в классе или вообще на независимые анонимные объекты делегирования.

Этот абзац из приведенной выше ссылки должен объяснить, что происходит в вашем коде:

Делегату может быть назначен любой метод, соответствующий сигнатуре делегата, которая состоит из возвращаемого типа и параметров. Это позволяет программно изменять вызовы методов, а также подключать новый код к существующим классам. Если вам известна подпись делегата, вы можете назначить свой собственный делегированный метод.

То есть при разрешении типов делегатов учитываются их сигнатуры, а не их имена.

В вашем случае ваши методы F1() and F2() , не принимающие параметров и ничего не возвращающие, имеют совпадающие подписи с Action делегатом без параметров:

 public delegate void Action();
  

Следовательно, они неявно преобразуются в Action .

Если вы попытаетесь передать метод с другим типом возвращаемого значения или хотя бы одним параметром, вы получите ошибку времени компиляции, поскольку она не будет соответствовать сигнатуре Action .

Комментарии:

1. И,, используя F1 вместо () => F1() , вызывает метод напрямую вместо добавления дополнительного делегата вокруг него.

2. @xixonia: Не очень «напрямую», компилятор Action тоже создает экземпляр, он Action создается из метода F1 напрямую. При использовании () => F1() , Action создается анонимный метод, в котором F1() вызывается.

3. @DannyChen, По крайней мере, более прямолинейно, чем нет. 🙂 Спасибо за исправление. Я волновался, что неправильно сформулировал это.

Ответ №2:

По сути, это своего рода то, что происходит в фоновом режиме:

 void Main() 
{    
    Flow.Sequence(new Action(delegate(){ F1(); }), new Action(delegate(){ F2(); }));    
    Flow.Sequence(new Action(F1), new Action(F2)); 
}
  

Они не СОВСЕМ эквивалентны, но они очень близки. Они будут выдавать одинаковые результаты во время выполнения, с той лишь разницей, что аргументы в первом вызове последовательности будут действием, которое вызывает анонимный метод, который затем вызывает статические методы F1 и F2; второй вызов последовательности будет действием, которое вызывает статические методы F1 и F2.

Я надеюсь, это поможет.

Комментарии:

1. Не беспокойтесь, я исправил это за вас.

Ответ №3:

Компилятор использует неявное преобразование из группы методов в делегат совместимого типа (в данном случае метод, возвращающий void, не принимающий аргументов), имена методов здесь неуместны.