Яким чином ви користуєтесь делегатами в C #?
Яким чином ви користуєтесь делегатами в C #?
Відповіді:
Тепер, коли в C # є лямбда-вирази та анонімні методи, я набагато більше використовую делегатів. У C # 1, де вам завжди доводилося мати окремий метод реалізації логіки, використання делегата часто не мало сенсу. Ці дні я використовую делегатів для:
Делегати дуже корисні для багатьох цілей.
Однією з таких цілей є використання їх для фільтрації послідовностей даних. У цьому випадку ви б використовували делегат предиката, який приймає один аргумент і повертає істинне або хибне в залежності від реалізації самого делегата.
Ось дурний приклад - я впевнений, що ви можете екстраполювати щось корисніше з цього:
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");
}
}
static Boolean inMyFamily(String name)
Метод делегата. Де бере в якості параметра делегат. Оскільки делегати - це лише покажчики функцій, коли ви передаєте ім'я методу в те, .Where(delegate)
що стає делегатом. Оскільки inMyFamily повертає булевий тип, він насправді вважається присудком. Предикати - це лише делегати, які повертають булеві.
Знайшов ще одну цікаву відповідь:
Співробітник просто задав мені це питання - який сенс делегатів у .NET? Моя відповідь була дуже короткою і такою, яку він не знайшов в Інтернеті: затримати виконання методу.
Джерело: LosTechies
Так само, як це робить LINQ.
Ви можете використовувати делегати для оголошення змінних і параметрів типу типу.
Приклад
Розглянемо схему "запозичення ресурсів". Ви хочете контролювати створення та очищення ресурсу, дозволяючи клієнтському коду "позичати" ресурс між ними.
Це оголошує тип делегата.
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] );
} );
Делегатів часто можна використовувати замість інтерфейсу з одним методом, загальним прикладом цього може бути модель спостерігача. Іншими мовами, якщо ви хочете отримати сповіщення про те, що щось сталося, ви можете визначити щось на кшталт:
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);
Я сподіваюся, що це допомагає!
Я заходжу на це дійсно пізно, але у мене виникли проблеми з з'ясуванням призначення делегатів сьогодні і написав дві прості програми, які дають той самий результат, який, на мою думку, добре пояснює їх призначення.
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);
}
}
Дещо інше використання - прискорити роздуми; тобто замість використання відображення кожного разу, ви можете використовувати Delegate.CreateDelegate
для створення (набраного) делегата методу (a MethodInfo
) і викликати цього делегата. Це тоді багато швидше за один дзвінок, оскільки перевірки вже зроблені.
З цим Expression
ви можете також зробити те ж саме, щоб створити код під час польоту - наприклад, ви можете легко створити Expression
оператор +, який представляє + оператора для типу, вибраного під час виконання (щоб надати підтримку оператора для дженериків, які мова не надає) ; і ви можете скласти Expression
до набраного делегата - робота виконана.
Делегати використовуються щоразу, коли ви використовуєте події - ось механізм, за яким вони працюють.
Крім того, делегати дуже корисні для таких речей, як використання запитів LINQ. Наприклад, багато запитів LINQ приймають делегата (часто Func<T,TResult>
), який можна використовувати для фільтрації.
Приклад може бути, як показано тут . У вас є метод обробки об’єкта, який відповідає певним вимогам. Однак ви хочете мати можливість обробляти об'єкт декількома способами. Замість того, щоб створювати окремі методи, ви можете просто призначити метод узгодження, який обробляє об'єкт, до делегата та передає делегат методу, який вибирає об'єкти. Таким чином, ви можете призначити різні методи одному методу селектора. Я намагався зробити це легко зрозумілим.
Я використовую делегатів для спілкування з потоками.
Наприклад, у мене може бути додаток із формами виграшу, який завантажує файл. Додаток запускає робочу нитку для завантаження (що не дозволяє GUI блокуватися). Робочий потік використовує делегатів для надсилання повідомлень про стан (наприклад, прогрес завантаження) назад до основної програми, щоб GUI міг оновити рядок стану.
Для обробника подій
Передати метод у параметри методу
Події, інші будь-які операції синхронізації
Кожен раз, коли ви хочете інкапсулювати поведінку, але посилайтеся на неї рівномірно. Обробники подій, функції зворотного дзвінка тощо. Ви можете виконати подібні речі за допомогою інтерфейсів та кастів, але іноді поведінка не обов'язково прив'язується до типу чи об'єкта . Іноді у вас просто така поведінка, яку потрібно інкапсулювати.
Ледача ініціалізація параметрів! Крім усіх попередніх відповідей (шаблон стратегії, модель спостерігача тощо), делегати дозволяють обробляти ледачу ініціалізацію параметрів. Наприклад, припустимо, у вас є функція Download (), яка займає досить багато часу і повертає певний DownloadedObject. Цей об'єкт споживається сховищем залежно від певних умов. Як правило, ви:
storage.Store(conditions, Download(item))
Однак з делегатами (точніше, лямбдами) ви можете зробити наступне, змінивши підпис магазину, щоб він отримав Умова та функцію <Item, DownloadedObject> та використовував його так:
storage.Store(conditions, (item) => Download(item))
Отже, сховище буде оцінювати делегата лише у разі необхідності, виконуючи завантаження залежно від Умов.
Використання делегатів
Параметр порівняння в масиві In Array.Sort (T [], Порівняння порівняння), List.Sort (Порівняння порівняння) тощо
Наскільки мені відомо, делегати можуть бути перетворені на функціональні покажчики. Це робить життя набагато простішим при взаємодії з нативним кодом, який приймає функціональні вказівники, оскільки вони можуть бути ефективно орієнтовані на об'єкти, навіть якщо оригінальний програміст не передбачив жодного положення для цього.
Делегат використовується для виклику методу за його посиланням. Наприклад:
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();
}
}