Как правильно использовать делегатов / понимание делегатов

использовать-C# (.Net Framework 4.5, Visual Studio 2012)

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

Как я понимаю, я должен сделать несколько вещей для использования делегата:

  • создайте некоторую сущность для работы с ней (что требует создание некоторого делегата)
  • объявить тип делегата
  • создать какой-то метод, где я вызываю делегировать
  • в основном делегате вызова класса с требуемым методом, который использует сущность (из первой точки)

все описанное я показываю ниже

Understanding delagates

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

и другое вопрос в отношении Делегат-где лучше поместить код с делегатом - в консольном приложении C# я могу создать его в любом месте используемого пространства имен - как я могу показать ниже.

placing of dalagete

но, возможно, есть некоторые рекомендации/требования для размещения делегата не только для консольного приложения, но и для WinForms, WPF и т. д.

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

редактировать

namespace SimpleCSharpApp
{
   delegate void myDelagate ();
}

3 ответов


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

во-первых, забудь о public void delegate zczcxxzc линия на мгновение. Это нечто особенное. Во-первых, давайте посмотрим на некоторые стандартные виды *) делегатов.

два самых основных из них:

  • System.Action
  • System.Func

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

для начала, давайте ограничимся голым, без параметров,System.Action.

private static void myFunction1() { Console.WriteLine("Hello!"); }
private static void myFunction2() { Console.WriteLine("Bye!"); }
private static void myFunction0() { return; }

... // in some function, i.e. Main()
Action myDelegate = null;

myDelegate = new Action( myFunction1 );
myDelegate(); // writes "Hello!"

myDelegate = new Action( myFunction2 );
myDelegate(); // writes "Bye!"

myDelegate = new Action( myFunction3 );
myDelegate(); // does "nothing"

так же, как" int "содержит число," string " - текст," делегат "содержит информацию о" чем-то вызываемом "или, используя некоторую терминологию,"что-то вызываемое".

Во-Первых, Я создать делегат типа "действие", которое запоминает "myFunction1". Затем я вызываю / вызываю этот делегат - это приводит к вызову запомненной функции.

Потом, Я создать делегат типа "Action", который запоминает "myFunction2". Затем я вызываю / вызываю этот делегат - это приводит к вызову запомненной функции.

Наконец-То, Я создать делегат типа "Action", который запоминает "myFunction3". Затем я вызываю / вызываю этот делегат - это приводит к вызову запомненной функции, и ничего не происходит - но только потому, что целевая функция ничего не делал.

обратите внимание, что я намеренно говорю "создал делегата". Каждый раз, когда new Action выполняется, создается новый делегат. "Делегат" - это просто объект, например строка" foo " или float[] {1.2, 4.5}.

кроме того, обратите внимание, что полный синтаксис для создания делегатов здесь используется new Action(...). Так же, как создание любого объекта - new + typename + параметры конструкции. Еще один признак того, что "делегат" - это просто объект.

еще одна вещь, чтобы отметить, что я не пишу new Action( myFunction1() ). Я не хотел вызывать метод и получать его результат и давать этот результат конструктору действия. Я написал new Action( myFunction1 ). Я дал сама функция в конструктор.

поэтому некоторые сравнивают делегатов с "указателями функций". Но это не совсем так. Указатели функций могут запоминать функции, чтобы позвонить. Делегаты могут вспомнить, что метод, чтобы позвонить. Помните разницу? В моем примере выше, я намеренно написал static в каждом myFunction. Их можно назвать object-less/target-less. Вам просто нужно их имя, и вы можете позвонить им из любого места. Чтобы вызвать их, простой тупой указатель был бы достаточно.

теперь, делегаты могут сделать больше. Они могут работать на методы. Но методы должны быть вызваны против объекта..

class GuineaPig
{
    public static void Squeak() { Console.WriteLine("Ieek!"); }

    public void Well() { Console.WriteLine("actually"); }
    public void IDontKnow() { Console.WriteLine("what they do"); }
}

GuineaPig.Squeak(); // says 'ieek'

Action myDelegate = null;
myDelegate = new Action( GuineaPig.Squeak );
myDelegate(); // writes "ieek"

// GuineaPig.Well(); // cannot do!
// myDelegate = new Action( GuineaPig.Well ); // cannot do!

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

но если вы попытаетесь раскомментировать ссылки на нестатические методы, он не будет компилироваться. Глядя на GuineaPig.Well - это очевидный. Это не статично, его нужно вызывать против объекта, а не класса. По этой же причине, делегат не может быть создан. Давайте исправим это:

class GuineaPig
{
    public void Well() { Console.WriteLine("actually"); }
    public void IDontKnow() { Console.WriteLine("what they do"); }
}

GuineaPig myPiggie = new GuineaPig();

myPiggie.Well(); // ok! writes "actually"

Action myDelegate = null;
myDelegate = new Action( myPiggie.Well ); // ok!

myDelegate(); // ok! writes "actually".

обратите внимание, как classname был заменен objectvariable во время создания делегата. Синтаксис сохраняется: точно так же, как вызов, но без parens. Но, что такое вся суета о "методах" против "функций"..

делегаты могут хранить не только "какой метод", но и что объект призвать их.

class GuineaPig
{
    public string Name;

    public void Well() { Console.WriteLine("I'm " + Name); }
}

GuineaPig myPiggie1 = new GuineaPig { Name = "Bubba" };
GuineaPig myPiggie2 = new GuineaPig { Name = "Lassie" };

Action myDelegate = null;

myDelegate = new Action( myPiggie1.Well );
myDelegate(); // -> Bubba

myDelegate = new Action( myPiggie2.Well );
myDelegate(); // -> Lassie

myPiggie1 = myPiggie2 = null;

myDelegate(); // -> Lassie

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

обратите внимание, как тот факт, что "хорошо" должен был быть вызван на "свинью#2", был сохранен внутри объекта делегата. Переменная "myPiggie2" не имеет значения. Я мог бы аннулировать его. Делегат помнил и цель, и метод.

:
var dd = new MyStudentFilteringDelegate ( ... ); // like normal class!
dd(); // ok!;

поскольку класс является специальным, генерируемым компилятором, он может нарушать правила. Это оператор 'call' / 'invoke' перегружен, поэтому вы можете "вызвать" делегат как метод.

обратите внимание, что, несмотря на странную нотацию:

public bool delegate MyStudentFilteringDelegate( Student stud );

the MyStudentFilteringDelegate это класс, так же, как Action или Func или String или WebClient. The delegate ключевое слово это просто маркер для компилятора, чтобы знать, какое преобразование он должен применить к этой строке, чтобы создать правильный "тип делегата" (класс).

теперь, чтобы фактически ответить на ваш другой вопрос:

это действительно неважно, куда вы положили объявление типа делегата. Вы можете написать public void delegate XYZ(...) где угодно. Так же, как вы можете разместить декларации класс почти везде.

вы можете поставить класс объявления в области по умолчанию (без пространства имен), в некотором пространстве имен или внутри класса. Таким образом, поскольку тип делегата-это просто класс, вы также можете удалить новые типы делегатов в области по умолчанию(без пространства имен), в некотором пространстве имен или внутри класса:

public class Xc {}
public void delegate Xd();

namespace WTF {
    public class Xc {}
    public void delegate Xd();

    class Whatever {
        public class Xc {}
        public void delegate Xd();
    }
}

обратите внимание, что я совершенно сознательно назвал их одинаково. Это не ошибка. Первые называются ::global.Xc и ::global.Xd, вторая пара называется WTF.Xc и WTF.Xd и последняя пара называется WTF.Whatever.Xc и WTF.Whatever.Xd. Как нормальные уроки.

чтобы решить, где разместить эти объявления, используйте те же правила, что и для классов. То есть. если вы размещаете классы обработки текста в пространстве имен MyApp.Text.Parsing, тогда все delegatetypes, связанных с обработкой текстов должен сидеть в этом пространстве тоже. Но даже в этом случае это чисто космический и организационный подход. Поместите / определите их в любом объеме, который имеет смысл для вас.

изменить: *) на самом деле, исторически все было наоборот. The delegate ключевое слово и компилятор трюк старше чем классы Action и Func. В .Net 2.0 действие / функция не существовали. Единственный способ создать / использовать делегат - определить свой собственный новый тип делегата (или найти/угадать подходящий где-то глубоко в пространствах имен системы). Имейте в виду, что каждый новый тип делегата является новым классом. Не конвертируемый ни в какой другой класс, даже не похожий на него. Это было так удручающе утомительно и трудно, что в .Net 3.5 они, наконец, включены "универсальные типы делегатов общего назначения" в структуру. С этого момента Action / Func все чаще используются, потому что даже если их труднее читать, они есть .. универсальный. System.Func<Student,bool> может быть передан "в любом месте", и у вас нет проблемы, что 'bool делегат StudentFilter()from one library does not matchbool делегат StudentSelector () ' от другого.


делегаты в C# немного похожи на замену указателей функций в C++. Для них существует множество обычаев. Вы можете использовать их для:

  1. Crate встроенная реализация для события.
  2. передайте обратный вызов функции.
  3. создайте массив "функций", которые вы можете вызвать в цикле.

из моего опыта Первое использование является наиболее распространенным.

Button.Click += delegate(object sender, EventArgs args)  => {/*you do something here*/};

которое может быть упрощено lamba выражение:

Button.Click += (sender, args) => {/*you do something here*/};

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

Что касается второй части вашего вопроса, я обычно помещаю объявления делегатов в отдельные файлы.


объявления делегатов могут быть размещены в любом исходном файле с общедоступной и внутренней доступностью. Я лично помещаю их в верхнюю часть источника класса, для которого он наиболее применим.

например, если у меня есть делегат специализированного события, который принимает CustomEventArgs в качестве параметра, я помещу объявление делегата в верхнюю часть этого файла:

namespace MyNamespace {
   public delegate void SpecialEventDelegate(object sender, CustomEventArgs e);

   public class CustomEventArgs : EventArgs {
      // implementation details
   }
}

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

делегат обычно имеет две квалификации:

  • объявление метода или члена класса
  • ссылка на метод или член класса

первая квалификация-это объявление делегата:

   public delegate void SpecialEventDelegate(object sender, CustomEventArgs e);

... или используя Func<TResult> или Action:

   Action<object, CustomEventArgs> // similar declaration to the SpecialEventDelegate above

поскольку обработчик событий (объявлен делегат) обычно не имеет возвращаемого типа (void), a Func<TResult> не будет использоваться. Делегату Func требуется тип возврата:

   Func<bool> // a delegate that return true/false

см. связанные статьи MSDN для получения дополнительной информации о Func<TResult> и Action. Я включил ссылки на них только для полноты и понимания новых методов объявления. Func<TResult> и Action специализированные оболочки для delegate.

вторая квалификация для делегат-это ссылка на метод или член класса. Я могу имейте частный метод, который действует как обработчик для определенных потребностей. Скажем, объекту FileIO нужен определенный обработчик файлов для разных типов файлов - то есть .XML. ,ФОРМАТ TXT. ,CSV:

namespace MyNamespace {

   public delegate Stream OpenFile(FileInfo FileSpec);

}

теперь любой объект может реализовать свой собственный OpenFile определение на основе типа файла, но должен возвратить