Коли ви використовуєте делегатів у C #? [зачинено]


101

Яким чином ви користуєтесь делегатами в C #?


2
Ви маєте на увазі представників у системі типу .NET або синтаксисі делегата C #? Ви маєте на увазі "коли ви використовуєте синтаксис делегата замість синтаксису лямбда-виразу" або ви маєте на увазі "коли ви використовуєте делегатів замість класів / інтерфейсів / віртуальних методів / тощо"?
Нікі

Відповіді:


100

Тепер, коли в C # є лямбда-вирази та анонімні методи, я набагато більше використовую делегатів. У C # 1, де вам завжди доводилося мати окремий метод реалізації логіки, використання делегата часто не мало сенсу. Ці дні я використовую делегатів для:

  • Обробники подій (для GUI та інших)
  • Початкові нитки
  • Зворотні дзвінки (наприклад, для API асинхронізації)
  • LINQ та подібні (List.Find тощо)
  • В іншому місці, де я хочу ефективно застосувати "шаблонний" код з певною спеціалізованою логікою всередині (де делегат забезпечує спеціалізацію)

Варто згадати про "push" у Push LINQ?
Марк Гравелл

3
Не впевнений, як би я пояснив це коротко, не роблячи речі більш заплутаними :) (Можливо, це все-таки охоплюється обробниками подій, LINQ та трохи шаблоном!
Джон Скіт

1
Ваше перше речення не має великого сенсу.
сенфо

3
Я знаю, що ви намагаєтесь сказати, але мені було б легше читати наступне: "Тепер, коли у C # є вирази лямбда та анонімні методи, я набагато більше використовую делегатів". Я знаю, що я випиваю, але мені справді довелося прочитати це речення кілька разів, перш ніж це мало сенс для мене.
сенфо

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

@LandonPoch цей коментар було б краще розмістити у відповіді. я, як початківець, не міг зрозуміти, де це було. Дякую.
Eakan Gopalakrishnan

@Eakan, я не відповідав на головне питання (коли ви використовуєте делегатів), тому я залишив це як коментар.
Landon Poch

14

Знайшов ще одну цікаву відповідь:

Співробітник просто задав мені це питання - який сенс делегатів у .NET? Моя відповідь була дуже короткою і такою, яку він не знайшов в Інтернеті: затримати виконання методу.

Джерело: LosTechies

Так само, як це робить LINQ.


+ 1..Не думав про це так. Хороший момент
Лука101

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);

Я сподіваюся, що це допомагає!


Приємні приклади з синтаксисом .. Дякую .. :)
Рагу

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

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

Наприклад, у мене може бути додаток із формами виграшу, який завантажує файл. Додаток запускає робочу нитку для завантаження (що не дозволяє GUI блокуватися). Робочий потік використовує делегатів для надсилання повідомлень про стан (наприклад, прогрес завантаження) назад до основної програми, щоб GUI міг оновити рядок стану.



0

Перший рядок використання - це заміна шаблону спостерігача / спостережуваного (події). Другий, приємний елегантний варіант шаблону стратегії. Можна зібрати різні інші звичаї, хоча й езотеричніші, ніж ці перші два, на мою думку.


0

Події, інші будь-які операції синхронізації


0

Кожен раз, коли ви хочете інкапсулювати поведінку, але посилайтеся на неї рівномірно. Обробники подій, функції зворотного дзвінка тощо. Ви можете виконати подібні речі за допомогою інтерфейсів та кастів, але іноді поведінка не обов'язково прив'язується до типу чи об'єкта . Іноді у вас просто така поведінка, яку потрібно інкапсулювати.


0

Ледача ініціалізація параметрів! Крім усіх попередніх відповідей (шаблон стратегії, модель спостерігача тощо), делегати дозволяють обробляти ледачу ініціалізацію параметрів. Наприклад, припустимо, у вас є функція Download (), яка займає досить багато часу і повертає певний DownloadedObject. Цей об'єкт споживається сховищем залежно від певних умов. Як правило, ви:

storage.Store(conditions, Download(item))

Однак з делегатами (точніше, лямбдами) ви можете зробити наступне, змінивши підпис магазину, щоб він отримав Умова та функцію <Item, DownloadedObject> та використовував його так:

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

Отже, сховище буде оцінювати делегата лише у разі необхідності, виконуючи завантаження залежно від Умов.


Незначна точка, але знову "точніше, лямбда" - ви могли б зробити те ж саме з анонімним методом у C # 2.0, хоча це було б більш багатослівним: делегат (itemType item) {[return] Завантажити (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.