Может ли кто-то перевести на правильный английский, что такое делегат? - PullRequest
17 голосов
/ 30 марта 2010

Может ли кто-нибудь объяснить, что такое делегат, в простом, кратком и кратком объяснении, которое охватывает как цель, так и общие преимущества? Я пытался обернуть голову вокруг этого, и он просто не тонет.

Ответы [ 11 ]

30 голосов
/ 30 марта 2010

У меня есть функция:

public long GiveMeTwoTimesTwo()
{
    return 2 * 2;
}

Эта функция отстой. Что если я хочу 3 * 3?

public long GiveMeThreeTimesThree()
{
    return 3 * 3;
}

Слишком много печатать. Я ленивый!

public long SquareOf(int n)
{
    return n * n;
}

Моя SquareOf функция не имеет значения, что такое n. Он будет работать правильно для любого переданного n. Он не знает точно, что такое число n, но он знает , что n является целым числом. Вы не можете передать "Haha not an integer" в SquareOf.

Вот еще одна функция:

public void DoSomethingRad()
{
    int x = 4;
    long y = SquareOf(x);
    Console.WriteLine(y);
}

Вопреки своему названию, DoSomethingRad фактически ничего не делает, рад. Тем не менее, он пишет SquareOf (4), который равен 16. Можем ли мы изменить его на менее скучный?

public void DoSomethingRad(int numberToSquare)
{
    long y = SquareOf(numberToSquare);
    Console.WriteLine(y);
}

DoSomethingRad явно все еще довольно неудачно. Но, по крайней мере, теперь мы можем передать число в квадрат, чтобы оно не записывало 16 каждый раз. (Он напишет 1, или 4, или 9, или 16, или ... zzzz все еще скучно).

Было бы хорошо, если бы был способ изменить то, что происходит с переданным числом. Может быть, мы не хотим ставить его в квадрат; может быть, мы хотим его кубировать или вычесть из 69 (число, выбранное случайным образом из моей головы).

При дальнейшем осмотре кажется, что единственная часть SquareOf, о которой DoSomethingRad заботится, - это то, что мы можем дать ей целое число (numberToSquare) и что она дает нам long (потому что мы ставим его возвращаемое значение в y и y равно long).

public long CubeOf(int n)
{
    return n * n * n;
}

public void DoSomethingLeet(int numberToSquare)
{
    long y = CubeOf(numberToSquare);
    Console.WriteLine(y);
}

Посмотрите, насколько DoSomethingLeet похож на DoSomethingRad? Если бы только был способ передать поведение (DoX()) вместо просто data (int n) ...

Так что теперь, если мы хотим написать квадрат числа, мы можем DoSomethingRad, а если мы хотим написать куб числа, мы можем DoSomethingLeet. Итак, если мы хотим написать число, вычтенное из 69, нужно ли нам делать другой метод, DoSomethingCool? Нет, потому что это чертовски много печатает (и что еще более важно, это мешает нам изменять интересное поведение, изменяя только один аспект нашей программы).

Итак, мы приходим к:

public long Radlicious(int doSomethingToMe, Func<int, long> doSomething)
{
    long y = doSomething(doSomethingToMe);
    Console.WriteLine(y);
}

Мы можем вызвать этот метод, написав это:

Radlicious(77, SquareOf);

Func<int, long> - особый вид делегата. Он хранит поведение, которое принимает целые числа и выплевывает long с. Мы не уверены, что метод, на который он указывает, будет делать с любым заданным нами целым числом; все, что мы знаем, это то, что, что бы ни случилось, мы вернем long.

Нам не нужно задавать какие-либо параметры для SquareOf, потому что Func<int, long> описывает поведение , а не данные. Вызов Radlicious(77, SquareOf) просто дает Radlicious общее поведение SquareOf («Я беру число и возвращаю его квадрат»), а не то, что SquareOf сделает с любым конкретным целым числом.

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

* КОНЕЦ ОТВЕТА, НАЧИНАЮТ СТРАНУЮ ИДИОЦИЮ *

Я имею в виду, кажется, что int s может восприниматься как просто очень скучное поведение:

static int Nine()
{
    return 9;
}

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

Конечно, можно представить супер «интересное» поведение, которое принимает все виды абстрактных параметров, но требует, чтобы тонна информации могла его вызвать. Что если нам потребуется предоставить исходный код, который он скомпилирует и запустит для нас?

Что ж, тогда наша абстракция, кажется, вернула нас обратно на круги своя. У нас такое абстрактное поведение, что для определения того, что он собирается делать, требуется весь исходный код нашей программы. Это полностью неопределенное поведение: функция может делать что угодно , но для определения того, что она делает, ей нужно предоставить everything . С другой стороны, полностью определенное поведение, такое как Nine(), не нуждается в дополнительной информации, но не может ничего сделать, кроме возврата 9.

И что? Я не знаю.

21 голосов
/ 30 марта 2010

Проще говоря, это по сути указатель на метод.

У вас может быть переменная, которая содержит тип делегата (точно так же, как у вас была бы переменная int, которая может содержать тип int). Вы можете выполнить метод, на который указывает делегат, просто вызвав вашу переменную как функцию.

Это позволяет вам иметь переменные функции так же, как вы можете иметь переменные данные. Ваш объект может принимать делегатов от других объектов и вызывать их без необходимости определения всех возможных функций.

Это очень удобно, когда вы хотите, чтобы объект выполнял какие-либо действия в соответствии с заданными пользователем критериями. Например, фильтрация списка на основе определенного пользователем выражения true / false. Вы можете позволить пользователю указать функцию делегата, которая будет использоваться в качестве фильтра для оценки каждого элемента списка.

3 голосов
/ 30 марта 2010

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

3 голосов
/ 30 марта 2010

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

здесь - ссылка на простое руководство.

У меня был вопрос: «Так зачем мне это делать?» Вы действительно не «поймете это», пока не решите с ними проблему программирования.

2 голосов
/ 30 марта 2010

Это просто ссылка на метод. Они очень полезны при работе с поперечной резьбой.

Вот пример прямо из моего кода.

 //Start our advertisiment thread
    rotator = new Thread(initRotate);
    rotator.Priority = ThreadPriority.Lowest;
    rotator.Start();

    #region Ad Rotation
    private delegate void ad();
    private void initRotate()
    {
        ad ad = new ad(adHelper);
        while (true)
        {
            this.Invoke(ad);
            Thread.Sleep(30000);
        }

    }

    private void adHelper()
    {
        List<string> tmp = Lobby.AdRotator.RotateAd();
        picBanner.ImageLocation = @tmp[0].ToString();
        picBanner.Tag = tmp[1].ToString();            
    }
    #endregion

Если бы вы не использовали делегата, вы не смогли бы перехватить и вызвать функцию Lobby.AdRotator.

2 голосов
/ 30 марта 2010

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

Наиболее распространенное использование таких экземпляров делегатов - поддержка концепции обратных вызовов на уровне языка.

1 голос
/ 30 марта 2010

В самых простых терминах ответственность за выполнение метода делегируется другому объекту. Скажем, президент какой-то страны умирает, а президент США должен присутствовать на похоронах с соболезнованиями. Если президент США не сможет уйти, он передаст эту ответственность кому-то, либо вице-президенту, либо министру штата.

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

например.

Class Person
{
   public string GetPersonName(Person person)
   {
     return person.FirstName + person.LastName;
   }

   //Calling the method without the use of delegate
   public void PrintName()
   {
      Console.WriteLine(GetPersonName(this));
   }

   //using delegate
   //Declare delegate which matches the methods signature
   public delegate string personNameDelegate(Person person);

  public void PrintNameUsingDelegate()
  {
      //instantiate
      personNameDelegate = new personNameDelegate(GetPersonName);

      //invoke
      personNameDelegate(this);
  }

}

Метод GetPersonName вызывается с использованием объекта делегата personNameDelegate. В качестве альтернативы мы можем использовать метод PrintNameUsingDelegate, чтобы принимать делегата в качестве параметра.

public void PrintNameUsingDelegate(personNameDelegate pnd, Person person)
{
   pnd(person);
}

Преимущество состоит в том, что если кто-то хочет напечатать имя как lastname_firstname, он должен просто обернуть этот метод в personNameDelegate и перейти к этой функции. Никакого дальнейшего изменения кода не требуется.

Делегаты особенно важны в

  1. События
  2. Асинхронные вызовы
  3. LINQ (как лямбда-выражения)
1 голос
/ 30 марта 2010

В самых основных терминах делегат - это просто переменная, которая содержит (ссылку на) функцию. Делегаты полезны, потому что они позволяют передавать функцию как переменную, не заботясь о «откуда» функции.

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

class Foo
{
    public string Bar
    {
        get;
        set;
    }

    public void Baz()
    {
        Console.WriteLine(Bar);
    }
}

Foo foo = new Foo();
Action someDelegate = foo.Baz;

// Produces "Hello, world".
foo.Bar = "Hello, world";
someDelegate();
1 голос
/ 30 марта 2010

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

0 голосов
/ 06 апреля 2010

В самых простых терминах, которые я могу придумать, это: делегат перекладывает бремя работы в руки класса, который в значительной степени знает, что делать. Думайте об этом как о ребенке, который не хочет вырасти, чтобы полностью походить на своего старшего брата, но все еще нуждается в его руководстве и приказах. Вместо того, чтобы наследовать все методы от своего брата (то есть создавать подклассы), он просто заставляет своего брата выполнять работу, или Маленький брат делает то, что требует действий, которые должен предпринять старший брат. Когда вы попадаете в рамки протоколов, старший брат определяет, что абсолютно необходимо, или он может дать вам гибкость в выборе того, что вы хотите, чтобы он делал в определенных событиях (т.е. неформальные и формальные протоколы, как описано в Objective-C).

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

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