События
события - это на самом деле одна из вещей, которые мне действительно нравятся в .net, потому что они позволяют вам объявить намного более чистый интерфейс. У вас может быть класс президента, который объявляет, что ему нужен ответ, не привязывая его к реализации отвечающего агента, например
interface IPresident
{
event Action<QuestionArgs, IPresident> HasQuestion;
void RecieveAnswer(QuestionArgs,Answer);
}
и затем в классе вашего ученого
partial class Scientist
{
public Scientist(IPresident president)
{
president.HasQuestion += TryToAnswerQuestion;
}
private void TryToAnswerQuestion(QuestionArgs question, IPresident asker)
{
if(CanAnswerQuestion(question))
{
asker.RecieveAnswer(question,GetAnswer(question));
}
}
}
Если новый класс хочет ответить на вопросы президентов, все, что ему нужно сделать, - это прослушать событие, сигнализирующее о том, что есть вопрос, на который необходимо ответить, и затем ответить на него, если они смогут. Если ученый хочет отвечать на вопросы кого-то еще, нам просто нужно реализовать метод, который прикрепляется к их Событию.
прямой вызов делегата
Проблема с делегированным подходом, который вы описали выше, заключается в том, что он нарушает инкапсуляцию. Он тесно связывает реализации ученых и президента и делает код хрупким. Что происходит, когда у вас есть другой человек, который отвечает на вопросы? В вашем примере вам нужно будет изменить реализацию Scientist, чтобы добавить новую функциональность, это называется «хрупким» кодом и является плохой вещью. Эта техника играет определенную роль в композиции, но она будет редко, если вообще когда-либо, лучшим выбором.
случай linq отличается, потому что вы не выставляете делегата как члена класса / интерфейса. Вместо этого вы используете его как функтор, объявленный вызывающей стороной, чтобы сообщить вам, какая информация интересует вызывающего. Поскольку вы выполняете инкапсуляцию «туда-обратно», она остается неизменной.
Это позволяет вам определять очень чистые и мощные API.
Мы могли бы взять пример Ученого и расширить его, используя эту технику, чтобы позволить кому-то выяснить, на какие вопросы мы можем ответить следующим образом
partial class Scientist
{
public IEnumerable<QuestionArgs> FindQuestions(Predicate<QuestionArgs> interest, IPresident asker)
{
return this.Questions.Where( x => interest(x) == true && x.IsAuthorizedToAsk(asker))
}
}
// ...
partial class President
{
FirePhysicists()
{
foreach(var scientist in scientists)
{
if(scientist.FindQuestions(x => x.Catagory == QuestionCatagory.Physics, this).Count != 0)
{
scientist.Fire();
}
}
}
}
Обратите внимание, что метод FindQuestions
позволил нам не реализовывать кучу другого кода для опроса ученого, который нам был бы необходим без возможности обмениваться делегатами. Хотя это не единственный случай, когда вы собираетесь находить делегатов, вызываемых напрямую, это один из самых распространенных