Делегаты - это просто очень удобный способ представления поведения. Вы можете обычно добиться того же самого, объявив интерфейс с одним методом ... но это часто будет намного более громоздким в использовании. (Особенно там, где вы обычно объединяете делегатов, создаете их с помощью лямбда-выражений или вызываете их асинхронно.)
Я довольно редко объявляю события, но часто использую их при написании кода пользовательского интерфейса. Я использую делегатов гораздо чаще, чем это, хотя - например, LINQ полностью основан на делегатах.
Я не думаю, что действительно полезно задавать вопросы, которые абсолютно невозможно решить без делегатов. Имеет больше смысла думать о проблемах, которые лучше решаются с делегатами - и это вопрос ситуаций, когда вам нужно уметь представлять один аспект поведения (реагирование на нажатие кнопки, фильтр или проекция для коллекции, действие, которое нужно предпринять, когда поток запущен и т. д.) настолько легко, насколько это возможно.
РЕДАКТИРОВАТЬ: Чтобы расширить комментарий интерфейса с одним методом, представьте себе этот интерфейс:
public interface IEventHandler
{
void HandleEvent(object sender, EventArgs args);
}
С помощью соответствующих классов поддержки, позволяющих объединить несколько экземпляров IEventHandler
в один (объединяя их в цепочку) и типом, представляющим «событие», вы можете в основном достичь того, что делает делегат EventHandler
... но способ, который гораздо сложнее использовать.