Creating delegates manually vs using Action/Func d

2019-01-04 10:51发布

Today I was thinking about declaring this:

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

but why not use this:

private Func<string, int, double> ChangeListAction;

or if ChangeListAction would have no return value I could use:

private Action<string,int> ChangeListAction;

so where is the advantage in declaring a delegate with the delegate keyword?

Is it because of .NET 1.1, and with .NET 2.0 came Action<T> and with .NET 3.5 came Func<T>?

8条回答
时光不老,我们不散
2楼-- · 2019-01-04 11:19

Declaring a delegate explicitly can help with some type checks. The compiler can make sure that the delegate assigned to the variable is intended to be used as ChangeListAction and not some random action that happens to be compatible with the signature.

However the real value of declaring your own delegate is that it gives it semantic meaning. A person reading the code will know what the delegate is doing by its name. Imagine if you had a class with three int fields but instead you declared an array of three int elements. The array can do the same thing but the names of the fields bring semantic information that is useful to the developers.

You should use Func, Predicate and Action delegates when you are designing a general purpose library like LINQ. In this case the delegates do not have a predefined semantics other than the fact that they will execute and action or be used as a predicate.

On a side note there is a similar tradeoff issue with Tuple vs anonymous type vs declaring your own class. You could just stick everything in a Tuple but then the properties are just Item1, Item2 which tells nothing about the use of the type.

查看更多
放我归山
3楼-- · 2019-01-04 11:22

As MSDN said, Func<> is itself pre-defined Delegate. For the first time, I confused about this stuff. After the experimental, my understanding was quite clearer. Normally, in C#, we can see

Type as a pointer to Instance.

The same concept is applied to

Delegate as a pointer to Method

The difference between these to things is Delegate do not possess the concept of OOP, for example, Inheritance. To make this things clearer, I did the experimental with

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;

Many build-in methods in framework (for example, LINQ), receive the parameter of Func<> delegate. The thing we can do with this method is

Declare the delegate of Func<> type and pass it to the function rather than Define the custom delegate.

For example, from the code above I add more code

string[] strList = { "abc", "abcd", "abcdef" };
strList.Select(fAssign); // is valid
//strList.Select(customDlg); // Compilation Error!!
查看更多
啃猪蹄的小仙女
4楼-- · 2019-01-04 11:25

For better and more elaborate answer look at @nawfal. I will try to be more simplistic.

You are declaring a member of a class so you should stick with delegate. Using delegate is more descriptive and structural.

Action/Func types are made for passing around so you should use them more as parameters and local variables.

And actually both of those are inheriting Delegate class. Action and Func are generic types and simplify creating delegates with different parameter types. And delegate keyword actually creates whole new class inheriting from Delegate in one declaration.

查看更多
何必那么认真
5楼-- · 2019-01-04 11:28

The advantage is clarity. By giving the type an explicit name it is more clear to the reader what it does.

It will also help you when you are writing the code. An error like this:

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

is less helpful than one which says:

cannot convert from CreateListAction to UpdateListAction

It also means that if you have two different delegates, both of which take the same types of parameters but conceptually do two entirely different things, the compiler can ensure that you can't accidentally use one where you meant the other.

查看更多
Fickle 薄情
6楼-- · 2019-01-04 11:32

Declare the delegate explicitly when you start to get too many parameters in the Func/Action, otherwise you keep having to look back, "What's the 2nd int mean again?"

查看更多
可以哭但决不认输i
7楼-- · 2019-01-04 11:33

I have found a special use case where you can only use delegate:

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

Using Func/Action just does not work: '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);

Following code does compile, but throws exception when running because System.Runtime.InteropServices.DllImportAttribute does not support marshaling of generic types:

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

I present this example to show to every one that: sometimes delegate is your only choice. And this is a reasonable answer to your question why not use Action<T>/Func<T> ?

查看更多
登录 后发表回答