Создание делегатов вручную и использование делегатов Action/Func



сегодня я думал об объявлении этого:



private delegate double ChangeListAction(string param1, int number);


но почему бы не использовать это:



private Func<string, int, double> ChangeListAction;


или ChangeListAction не будет иметь возвращаемого значения я мог бы использовать:



private Action<string,int> ChangeListAction;


так где же преимущество в объявлении делегата с delegate ключевое слово?



это из-за .NET 1.1, а с .NET 2.0 пришел Action<T> и с .NET 3.5 пришел Func<T>?

498   8  

8 ответов:

преимущество ясности. Давая типу явное имя, читателю становится более ясно, что он делает.

Он также поможет вам, когда вы пишете код. Ошибка вроде этой:

cannot convert from Func<string, int, double> to Func<string, int, int, double>

менее полезны, чем тот, который говорит:

cannot convert from CreateListAction to UpdateListAction

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

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

  1. как указывали другие, передает намерение явно в отличие от общего Action и Func (Патрик имеет очень хороший момент о значимых именах параметров).

  2. вы можете указать ref/out параметры в отличие от двух других общих делегаты съезда. Например, вы можете иметь

    public delegate double ChangeListAction(out string p1, ref int p2);
    

    а не

    Func<out string, ref int, double> ChangeListAction;
    
  3. кроме того, с пользовательскими делегатами вам нужно написать ChangeListAction (я имею в виду определение) только один раз где-то в вашей базе кода, тогда как если вы не определяете его, вам придется засорять везде Func<string, int, double> во всем. Изменение подписи будет хлопот в последнем случае-плохой случай не быть сухим.

  4. может иметь необязательные параметры.

    public delegate double ChangeListAction(string p1 = "haha", int p2);
    

    а не

    Func<string, int, double> ChangeListAction = (p1 = "haha", p2) => (double)p2; 
    
  5. вы можете params ключевое слово для параметров метода, не так с Action/Func.

    public delegate double ChangeListAction(int p1, params string[] p2);
    

    а не

    Func<int, params string[], double> ChangeListAction;
    
  6. Ну, если вам действительно не повезло и нужны параметры более 16 (на данный момент):)


что касается достоинств Action и Func:

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

  2. что еще более важно, его тип совместим между доменами. Action и Func определены рамки, и они работают плавно, пока типы параметров совпадают. Вы не можете иметь ChangeSomeAction на ChangeListAction. Linq находит большое использование этого аспекта.

объявление делегата явно может помочь с некоторыми проверками типа. Компилятор может убедиться, что делегат, назначенный переменной, предназначен для использования в качестве ChangeListAction, а не какого-либо случайного действия, совместимого с сигнатурой.

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

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

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

поскольку в некоторых ответах упоминается, что выигрыш-это ясность, вы называете тип, и поэтому пользователю вашего api будет легче понять. Я бы сказал-в большинстве случаев-объявить типы делегатов для ваших публичных API, но это вполне нормально использовать Func<?,?> внутренне.

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

Я нашел специальный случай использования, где вы можете использовать только делегат:

public delegate bool WndEnumProc(IntPtr hwnd, IntPtr lParam);
[DllImport("User32.dll")]
public static extern bool EnumWindows(WndEnumProc lpEnumFunc, IntPtr lParam);

использование Func / Action просто не работает:'Namespace.Class.WndEnumProc' is a 'field' but is used like a 'type':

public Func<IntPtr, IntPtr, bool> WndEnumProc;
[DllImport("User32.dll")]
public static extern bool EnumWindows(WndEnumProc lpEnumFunc, IntPtr lParam);

следующий код компилируется, но выдает исключение при запуске, потому что System.Runtime.InteropServices.DllImportAttribute не поддерживает маршалинг универсальных типа:

[DllImport("User32.dll")]
public static extern bool EnumWindows(Func<IntPtr, IntPtr, bool> lpEnumFunc, IntPtr lParam);

Я представляю этот пример, чтобы показать каждому, что: иногда делегат - это ваш единственный выбор. И это разумный ответ на ваш вопрос why not use Action<T>/Func<T> ?

объявите делегат явно, когда вы начинаете получать слишком много параметров в Func / Action, иначе вам придется оглядываться назад: "что означает 2-й int?"

для лучшего и более подробного ответа посмотрите на @nawfal. Я постараюсь быть более упрощенным.

вы объявляете член класса, так что вы должны придерживаться делегата. Используя delegate является более содержательным и структурным.

Action/Func типы предназначены для передачи, поэтому вы должны использовать их больше как параметры и локальные переменные.

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

как MSDN сказал:Func<> сам по себе предопределен Delegate. Впервые я запутался в этом материале. После эксперимента мое понимание было довольно ясным. Обычно в C# мы видим

Type как указатель на Instance.

то же самое понятие применяется к

Delegate как указатель на Method

разница между этими вещами есть Delegate do не обладают понятием ООП, например,Inheritance. Чтобы сделать это вещи яснее, я сделал эксперимент с

public delegate string CustomDelegate(string a);

// Func<> is a delegate itself, BUILD-IN delegate
//==========
// Short Version Anonymous Function
//----------
Func<string, string> fShort = delegate(string a)
{
  return "ttt";
};
// Long Version Anonymous Function
//----------
Func<string, string> fLong = a => "ttt";

MyDelegate customDlg;
Func<string, string> fAssign;
// if we do the thing like this we get the compilation error!!
// because fAssign is not the same KIND as customDlg
//fAssign = customDlg;

многие встроенные методы в framework (например, LINQ), получают параметр Func<> делегат. Что мы можем сделать с помощью этого метода

Declare представитель Func<> введите и передайте его в функцию, а не Define пользовательский делегат.

например, из кода выше, я еще добавлю код

string[] strList = { "abc", "abcd", "abcdef" };
strList.Select(fAssign); // is valid
//strList.Select(customDlg); // Compilation Error!!

Comments

    Ничего не найдено.