Когда бы вы использовали делегаты в C #? [закрыто]


101

Как вы используете делегаты в C #?


2
Вы имеете в виду делегатов в системе типов .NET или синтаксис делегатов C #? Вы имеете в виду «когда вы используете синтаксис делегата вместо синтаксиса лямбда-выражения» или имеете в виду «когда вы используете делегаты вместо классов / интерфейсов / виртуальных методов / и т. Д.»?
Ники

Ответы:


100

Теперь, когда у нас есть лямбда-выражения и анонимные методы в C #, я гораздо чаще использую делегаты. В C # 1, где всегда приходилось иметь отдельный метод для реализации логики, использование делегата часто не имело смысла. В наши дни я использую делегатов для:

  • Обработчики событий (для графического интерфейса и др.)
  • Начальные темы
  • Обратные вызовы (например, для асинхронных API)
  • LINQ и аналогичные (List.Find и т. Д.)
  • В любом другом месте, где я хочу эффективно применить "шаблонный" код с некоторой специализированной логикой внутри (где делегат предоставляет специализацию)

Стоит ли упомянуть «push» в Push LINQ?
Марк Гравелл

3
Не уверен, как бы я объяснил это вкратце, чтобы не усложнять ситуацию :) (Возможно, это все равно покрыто обработчиками событий, LINQ и шаблоном!
Джон Скит

1
Ваше первое предложение не имеет особого смысла.
senfo

3
Я знаю, что вы пытаетесь сказать, но мне было бы легче прочитать следующее: «Теперь, когда у нас есть лямбда-выражения и анонимные методы в C #, я гораздо чаще использую делегаты». Я знаю, что придираюсь к мелочам, но мне действительно пришлось прочитать это предложение несколько раз, прежде чем оно стало для меня осмысленным.
senfo

4
+1 за смелость бросить вызов достопочтенному мистеру Скиту ;-)
indra

29

Делегаты очень полезны для многих целей.

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

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

using System;
using System.Linq;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<String> names = new List<String>
        {
            "Nicole Hare",
            "Michael Hare",
            "Joe Hare",
            "Sammy Hare",
            "George Washington",
        };

        // Here I am passing "inMyFamily" to the "Where" extension method
        // on my List<String>.  The C# compiler automatically creates 
        // a delegate instance for me.
        IEnumerable<String> myFamily = names.Where(inMyFamily);

        foreach (String name in myFamily)
            Console.WriteLine(name);
    }

    static Boolean inMyFamily(String name)
    {
        return name.EndsWith("Hare");
    }
}

11
static Boolean inMyFamily(String name)Метод делегата. Где принимает делегата в качестве параметра. Поскольку делегаты являются просто указателями на функции, когда вы передаете имя метода в объект, .Where(delegate)который становится делегатом. Поскольку inMyFamily возвращает логический тип, он фактически считается предикатом. Предикаты - это просто делегаты, возвращающие логические значения.
Лэндон Поч

4
«Предикаты - это просто делегаты, возвращающие логические значения». +1
daehaai 06

@LandonPoch этот комментарий лучше было бы поместить в ответ. я, будучи новичком, не мог понять, где это. Спасибо.
Eakan Gopalakrishnan

@Eakan, я на самом деле не отвечал на главный вопрос (когда вы использовали бы делегатов), поэтому я оставил его как комментарий.
Landon Poch

14

Нашел еще один интересный ответ:

Сотрудник только что задал мне этот вопрос - какой смысл делегатов в .NET? Мой ответ был очень коротким, и он не нашел его в сети: отложить выполнение метода.

Источник: LosTechies

Так же, как это делает LINQ.


+1 .. не думал об этом так. Хороший момент
Luke101

12

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

пример

Рассмотрим модель «заимствования ресурсов». Вы хотите контролировать создание и очистку ресурса, позволяя клиентскому коду «заимствовать» ресурс между ними.

Это объявляет тип делегата.

public delegate void DataReaderUser( System.Data.IDataReader dataReader );

Любой метод, соответствующий этой сигнатуре, может использоваться для создания экземпляра делегата этого типа. В C # 2.0 это можно сделать неявно, просто используя имя метода, а также используя анонимные методы.

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

public class DataProvider
{
    protected string _connectionString;

    public DataProvider( string psConnectionString )
    {
        _connectionString = psConnectionString;
    }

    public void UseReader( string psSELECT, DataReaderUser readerUser )
    {
        using ( SqlConnection connection = new SqlConnection( _connectionString ) )
        try
        {
            SqlCommand command = new SqlCommand( psSELECT, connection );
            connection.Open();
            SqlDataReader reader = command.ExecuteReader();

            while ( reader.Read() )
                readerUser( reader );  // the delegate is invoked
        }
        catch ( System.Exception ex )
        {
            // handle exception
            throw ex;
        }
    }
}

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

string sTableName = "test";
string sQuery = "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='" + sTableName + "'";

DataProvider.UseReader( sQuery,
    delegate( System.Data.IDataReader reader )
    {
        Console.WriteLine( sTableName + "." + reader[0] );
    } );

11

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

class IObserver{ void Notify(...); }

В C # это чаще всего выражается с помощью событий, где обработчик является делегатом, например:

myObject.SomeEvent += delegate{ Console.WriteLine("..."); };

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

myList.Where(i => i > 10);

Выше приведен пример синтаксиса лямбда, который также можно было бы записать следующим образом:

myList.Where(delegate(int i){ return i > 10; });

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

myFactory.RegisterFactory(Widgets.Foo, () => new FooWidget());
var widget = myFactory.BuildWidget(Widgets.Foo);

Надеюсь, это поможет!


Хорошие примеры с синтаксисом .. Спасибо .. :)
Raghu

10

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

NoDelegates.cs

using System;

public class Test {
    public const int MAX_VALUE = 255;
    public const int MIN_VALUE = 10;

    public static void checkInt(int a) {
        Console.Write("checkInt result of {0}: ", a);
        if (a < MAX_VALUE && a > MIN_VALUE)
            Console.WriteLine("max and min value is valid");
        else
            Console.WriteLine("max and min value is not valid");
    }

    public static void checkMax(int a) {
        Console.Write("checkMax result of {0}: ", a);
        if (a < MAX_VALUE)
            Console.WriteLine("max value is valid");
        else
            Console.WriteLine("max value is not valid");
    }

    public static void checkMin(int a) {
        Console.Write("checkMin result of {0}: ", a);
        if (a > MIN_VALUE)
            Console.WriteLine("min value is valid");
        else
            Console.WriteLine("min value is not valid");
        Console.WriteLine("");
    }
}

public class Driver {
    public static void Main(string [] args) {
        Test.checkInt(1);
        Test.checkMax(1);
        Test.checkMin(1);

        Test.checkInt(10);
        Test.checkMax(10);
        Test.checkMin(10);

        Test.checkInt(20);
        Test.checkMax(20);
        Test.checkMin(20);

        Test.checkInt(30);
        Test.checkMax(30);
        Test.checkMin(30);

        Test.checkInt(254);
        Test.checkMax(254);
        Test.checkMin(254);

        Test.checkInt(255);
        Test.checkMax(255);
        Test.checkMin(255);

        Test.checkInt(256);
        Test.checkMax(256);
        Test.checkMin(256);
    }
}

Delegates.cs

using System;

public delegate void Valid(int a);

public class Test {
    public const int MAX_VALUE = 255;
    public const int MIN_VALUE = 10;

    public static void checkInt(int a) {
        Console.Write("checkInt result of {0}: ", a);
        if (a < MAX_VALUE && a > MIN_VALUE)
            Console.WriteLine("max and min value is valid");
        else
            Console.WriteLine("max and min value is not valid");
    }

    public static void checkMax(int a) {
        Console.Write("checkMax result of {0}: ", a);
        if (a < MAX_VALUE)
            Console.WriteLine("max value is valid");
        else
            Console.WriteLine("max value is not valid");
    }

    public static void checkMin(int a) {
        Console.Write("checkMin result of {0}: ", a);
        if (a > MIN_VALUE)
            Console.WriteLine("min value is valid");
        else
            Console.WriteLine("min value is not valid");
        Console.WriteLine("");
    }
}

public class Driver {
    public static void Main(string [] args) {
        Valid v1 = new Valid(Test.checkInt);
        v1 += new Valid(Test.checkMax);
        v1 += new Valid(Test.checkMin);
        v1(1);
        v1(10);
        v1(20);
        v1(30);
        v1(254);
        v1(255);
        v1(256);
    }
}

5

Несколько другое использование - ускорение отражения; то есть вместо использования отражения каждый раз вы можете использовать Delegate.CreateDelegateдля создания (типизированного) делегата методу (a MethodInfo) и вместо этого вызывать этот делегат. Тогда это намного быстрее на вызов, так как проверки уже были выполнены.

С помощью Expressionвы также можете сделать то же самое для создания кода на лету - например, вы можете легко создать оператор, Expressionкоторый представляет оператор + для типа, выбранного во время выполнения (для обеспечения поддержки операторов для универсальных шаблонов, которые язык не предоставляет) ; и вы можете скомпилировать его Expressionв типизированный делегат - работа сделана.


5

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

Кроме того, делегаты очень полезны для таких вещей, как использование запросов LINQ. Например, многие запросы LINQ принимают делегат (часто Func<T,TResult>), который можно использовать для фильтрации.



2

Пример можно увидеть здесь . У вас есть метод обработки объекта, отвечающий определенным требованиям. Однако вы хотите иметь возможность обрабатывать объект несколькими способами. Вместо того, чтобы создавать отдельные методы, вы можете просто назначить соответствующий метод, обрабатывающий объект, делегату и передать делегат методу, который выбирает объекты. Таким образом, вы можете назначать разные методы одному методу селектора. Я постарался сделать это легко понятным.


1

Я использую делегатов для общения с потоками.

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



0

Первая линия использования - заменить шаблон Observer / Observable (события). Вторая, красивая элегантная версия паттерна Стратегия. Можно собрать множество других употреблений, хотя я думаю, более эзотерических, чем эти первые два.


0

События, другие любые операции


0

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


0

Ленивая инициализация параметров! Помимо всех предыдущих ответов (шаблон стратегии, шаблон наблюдателя и т. Д.), Делегаты позволяют вам обрабатывать ленивую инициализацию параметров. Например, предположим, что у вас есть функция Download (), которая занимает довольно много времени и возвращает определенный DownloadedObject. Этот объект используется Хранилищем в зависимости от определенных условий. Обычно вы бы:

storage.Store(conditions, Download(item))

Однако с делегатами (точнее, лямбдами) вы можете сделать следующее, изменив подпись хранилища так, чтобы оно получало Condition и Func <Item, DownloadedObject> и использовать его следующим образом:

storage.Store(conditions, (item) => Download(item))

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


Незначительный момент, но «точнее, лямбды» - вы могли бы сделать то же самое с анонимным методом в C # 2.0, хотя он был бы более подробным: delegate (ItemType item) {[return] Download (item);}
Марк Грейвелл

Конечно, как и LINQ: лямбда-выражения - это не более чем синтаксический сахар для делегатов. Они просто сделали делегатов более доступными.
Сантьяго Палладино,

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

Ну, лямбды также можно скомпилировать в выражения, которые полностью отличаются от делегатов. Но в вашем примере используется Func <,>, который можно использовать из анонимного метода. Выражения на C # 2.0 было бы крайне болезненно писать.
Марк Гравелл


0

Параметр сравнения в In Array.Sort (массив T [], сравнение сравнения), List.Sort (сравнение сравнения) и т. Д.


0

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


0

Делегаты используются для вызова метода по ссылке. Например:

  delegate void del_(int no1,int no2);
class Math
{
   public static void add(int x,int y)
   {
     Console.WriteLine(x+y);
   }
   public static void sub(int x,int y)
   {
     Console.WriteLine(x-y);
   }
}



    class Program
    {
        static void Main(string[] args)
        {
            del_ d1 = new del_(Math.add);
            d1(10, 20);
            del_ d2 = new del_(Math.sub);
            d2(20, 10);
            Console.ReadKey();
        }
    }
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.