Чтобы лишь программиста C# необходимости для каждого случая создавать собственные делегаты в библиотеке классов NET.Framework определены и реализованы несколько типов.
Самые распространенные из них - Action <T>, Predicate <T>, Func <T>.
Символ Т в имени делегата означает его обобщенную форму, маскирует собой любой тип, переданный делегату.
Action <T>
Один из самых распространённых делегатов.
Делегат не шаблонизированный, поэтому нет возможности объявить общий вид.
И в зависимости от количества необходимых передаваемых параметров в делегат, программист должен выбирать соответствующую форму его объявления.
В версии NET.Framework 3.5 есть 4 версии этого делегата, начиная с версии 4.0 – их уже 16.
Action, часто используется для обратного вызова:
static void Main(string[] args)
{
Action<double, double> actiondelegate;
actiondelegate = Sum;
Operation(11.88, 6.55, actiondelegate);
actiondelegate = Substract;
Operation(100.22, 9.64, actiondelegate);
Console.Read();
}
static void Operation(double a, double b, Action<double, double> actiondelegate)
{
if (a > b)
actiondelegate(a, b);
}
static void Sum(double a, double b)
{
Console.WriteLine("Сумма чисел: " + (a + b));
}
static void Substract(double a, double b)
{
Console.WriteLine("Разность чисел: " + (a - b));
}
Action – void-делегат. Все версии делегата возвращают void-значение. Это серьезное ограничение, поскольку делегат не должен использоваться для информации о методах, имеющих тип возвращаемого значения отличный от void.
Но, если программист использует его для хранения ссылки на не void-метод, то ошибки не будет, просто возвращаемое значение использовать не удастся, оно будет проигнорировано средой исполнения.
Для хранения ссылок на методы с любым возвращаемым значением используются делегаты: Func <T> и Predicate<T>.
Func<T>
Также как и Action, делегат Func не шаблонизирован, в разных версиях NET.FrameWork имеет от 4 до 16 видов объявлений, которые различаются количеством параметров.
Его общее объявление: TResult Func<in T, out TResult>(T arg).
Делегат возвращает значение любого пользовательского или встроенного типа (TResult) может принимать параметры любого типа (Т): от 1 до 16.
Func применяется как делегат, передаваемый в качестве параметра другому методу: для алгоритмов расчёта, сортировки или встроенных методов:
using System;
public class FuncExample
{
public static void Main()
{
Func<string, string> MethodConvert = ConvertUserString;
string str = "Владимир";
Console.WriteLine(MethodConvert(str));
}
private static string ConvertUserString(string impStr)
{
result = impStr + DateTime.Now.Hour.ToString();
return result.ToUpper();
}
}
Predicate <T>
Встроенный, нешаблонизированый тип делегата.
Имеет 16 версий объявления начиная с .NET Framewok4.0.
Как и в случае Func и Action версии делегата различаются количество входных параметров.
Выходной или возвращаемое значение всегда одно, типа bool, поэтому назначения делегата – передавать ссылку на метод, в котором реализован алгоритм сравнения объектов.
По функциональности полностью идентичен Func<T, bool>. Однако введен в библиотеку типов по двум причинам: исторической, поскольку в языке появился раньше, чем универсальные типы; и из стремления к строгому типизированную в framework, для создания специального типа для каждого круга задач.
using System;
using System.Collections.Generic;
public class Drink
{
private string title;
private int yearofbotling;
public Drink(string title, int year)
{
title = title;
yearofbotling = year;
}
public string Title
{
get { return title; }
}
public int Yearofbotling
{
get { return yearofbotling; }
}
}
public class Example
{
public static void Main()
{
Random rnd = new Random();
List<Drink> drinks = new List<Drink>();
drinks.AddRange( new Drink[] {
new Drink("Московская Сивуха", 2015),
new Drink("VielleBonSecours", 2016),
new Drink("Массандра", 1999),
new Drink("Шато Лафитэ", 1905),
new Drink("Ley.925", 2006) }
);
int[] years = { 1900, 1965, 1980, 2015 };
int foundedBeforeYear = years[rnd.Next(0, years.Length)];
Console.WriteLine("Напитки разлиты ранее {0}:", foundedBeforeYear);
foreach (var dr in drinks.FindAll( delegate (int x) { x.Yearofbotling <= foundedBeforeYear };)//Используем анонимный метод.
Console.WriteLine("{0}: {1}", dr.Title, dr.Yearofbotling);
}
}
В предыдущем примере использован анонимный метод в качестве инициализации делегата Predicate<Т>, запись:
delegate (int x) { x.Yearofbotling <= foundedBeforeYear }
неявно приводится компилятором к созданию делегата, инициализации его методом. Никто, однако, не мешает сделать метод именованным и явно передать его в делегат Predicate.
По сути весь тяжкий выбор для программиста между Predicate<T> и Func<T, bool> лежит в области семантики: в случае Predicate метод на основании инструкций вернет true или false, а в случае Func<T,bool> тип bool, а это не всегда одно и то же.
Комментариев нет:
Отправить комментарий