Делегаты против событий - PullRequest
8 голосов
/ 24 мая 2011

Когда класс не может (или не должен) что-то делать, тогда события или делегаты могут быть решением.

скажем

class President
  Event AskedQuestion(QuestionEventArgs)
  Delegate GetAnswerToQuestion

class Scientist
  AnswerToQuestion()

// delegate approach
myPresident.GetAnswerToQuestion = AddressOf myScientist.AnswerToQuestion
// called once myPresident need it

// event approach
myScientist.AnswerToQuestion(questionEventArgs) Handles President.AskedQuestion
{ 
   // executed once president asked a question
}

Здесь в подходе делегата метод Ученого - этоиспользуется непосредственно Президентским классом, если один Президент поднимает вопрос, и Ученый реагирует на него ответом.

В коде .NET Framework я не наблюдал, однако прямого использования делегатов,Неправильно ли использовать его напрямую, и если, почему?

Ответы [ 4 ]

9 голосов
/ 24 мая 2011

Это неправильно использовать его напрямую, и если, почему?

Нет, это не так.

Вот как я об этом думаю. Поля делегатов относятся к событиям, а строковые поля - к свойствам. То есть вы можете иметь:

class Car
{
    private string modelName;
    public string ModelName { get { return this.modelName; } }
    ...

Название модели логически собственность автомобиля. Когда кто-то спрашивает вас, на какой машине вы ездите, и вы говорите «Ford Focus», вы описываете его свойства. Вы не думаете о «Ford Focus» как о «поле» или «струне», вы думаете о нем как о названии своего рода автомобиля. В компьютерной программе строковое поле - это просто деталь реализации того, как хранится имя. Свойство может быть строкой или перечислением или чем-то еще; Дело в том, что логически у автомобилей есть названия моделей, а не строковые поля.

События и делегаты одинаковы. У автомобиля может быть событие «взорвать» (возможно, вы пишете видеоигру!), А событие «взорвать» реализуется полем типа делегата. Взрыв - это то, что машина логически делает; поле делегата - это механизм, с помощью которого реализуется событие.

Так что "неправильно" использовать делегатов напрямую? Нет, конечно нет. Не более, чем «неправильно» использовать строки напрямую. Иногда вам нужно манипулировать строками, которые не являются свойствами, а иногда вам нужно манипулировать делегатами, которые не являются событиями.

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

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

Существует множество вариантов использования делегатов в рамках.LINQ является ярким примером этого:

var result = someCollection.Where(input => input.MatchesSomeCriteria);

Where принимает делегата с определенной подписью, которая вызывается для определения, включать элемент в результат или нет.Наиболее распространенное использование - это ламба, как показано выше, но вы также можете передать метод:

string[] nums = new[]{ "1", "2", "3"};
int sum = nums.Select(int.Parse).Sum();

int.Parse соответствует требуемой подписи делегата, которую Select ожидает в этом случае (Func<string,int>), поэтому он будет вызываться для каждой из строк в nums.

Обычно, когда делегаты используются напрямую, они принимаются как входные данные для вызовов методов, которые будут их использовать.Хотя в некоторых местах они являются частью состояния потребителей (например, HttpListener имеет несколько свойств делегированных типов), но их не так много.

1 голос
/ 24 мая 2011

События

события - это на самом деле одна из вещей, которые мне действительно нравятся в .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 позволил нам не реализовывать кучу другого кода для опроса ученого, который нам был бы необходим без возможности обмениваться делегатами. Хотя это не единственный случай, когда вы собираетесь находить делегатов, вызываемых напрямую, это один из самых распространенных

1 голос
/ 24 мая 2011

работа с делегатами в необработанном виде может повлечь за собой некоторый шаблонный код (определение делегата, объявление необходимых переменных-членов и создание пользовательских методов регистрации / отмены регистрации для сохранения инкапсуляции и т.Проблема использования делегатов в необработанном виде в качестве механизма обратного вызова вашего приложения заключается в том, что если вы не определите переменные-члены делегата класса как частные, вызывающая сторона будет иметь прямой доступ к объектам делегата.Если бы это было так, вызывающая сторона могла бы переназначить переменную новому объекту делегата (фактически удалив текущий список вызываемых функций), и что еще хуже, вызывающая сторона могла бы напрямую вызывать список вызовов делегата.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...