Creating delegates manually vs using Action/Func delegates
Asked Answered
T

8

82

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>?

Talton answered 19/12, 2010 at 10:58 Comment(0)
C
66

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.

Culmiferous answered 19/12, 2010 at 11:2 Comment(1)
The Func<T1,T2,U> has a name like Func<T1,T2,T3,U> so should the compiler not say: Can not convert from Func1Name to Func2Name then it would NOT be less helpful.Talton
W
92

The advent of Action and Func family of delegates has rendered custom delegates less used, but the latter still finds uses. Advantages of custom delegates include:

  1. As others have pointed, conveys intent clearly unlike generic Action and Func (Patrik has a very good point about meaningful parameter names).

  2. You can specify ref/out parameters unlike the other two generic delegates. For eg, you can have

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

    but not

    Func<out string, ref int, double> ChangeListAction;
    
  3. Also, with custom delegates you need to write ChangeListAction (I mean the definition) only once somewhere in your code base, whereas if you don't define one you will have to litter everywhere Func<string, int, double> all over. Changing the signature will be a hassle in the latter case - a bad case of not being dry.

  4. Can have optional parameters.

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

    but not

    Func<string, int, double> ChangeListAction = (p1 = "haha", p2) => (double)p2; 
    
  5. You can have params keyword for parameters of a method, not so with Action/Func.

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

    but not

    Func<int, params string[], double> ChangeListAction;
    
  6. Well, if you're truly out of luck and need parameters more than 16 (for the moment) :)


As to merits of Action and Func:

  1. It's quick and dirty, and I use it all over. It makes code short if the use-case is trivial (custom delegates have gone out of fashion with me).

  2. More importantly, its type compatible across domains. Action and Func are framework defined, and they operates seamlessly as long as the parameter types match. You can't have ChangeSomeAction for ChangeListAction. Linq finds great use of this aspect.

Wollongong answered 15/5, 2013 at 10:41 Comment(16)
Here's another one where you cannot use Func - you cannot use Func if it has to return itself as mentionned here: #27989796Soekarno
thanks @Marwie. Helpful. Will add to my answer at some point.Wollongong
In example #5 params must be last parameter.Executor
@Executor you're right. Silly mistake. Anyone can edit answers anytime :)Wollongong
You alluded to this with "LINQ". I believe Action/Func, are the foundation of anonymous (closure) methods.Olli
@Todd I dont get you tbh. My answer isnt about LINQ.Wollongong
Sorry, I tried too hard to keep my comment short. You are alluding to a very important reason why Action/Func are there at all. The reason "WHY" contributes a lot toward understanding the differences between Action/Func and delegate. You said Linq finds great use of this aspect.. Highlighting: Action and Func operate seamlessly across domains. I am saying there's more to this: Action/Func were fundamentaly created for anonymous (closure) methods (and Expressions) which enable LINQ and Fluent-like coding interfaces in C#.Olli
@Todd Having built-in delegates (like Func) were a necessity for LINQ to work (both for to-objects as well as to-entities) and generally useful to have built-in types, whereas closures existed in C# even before .NET 3.5. Action/Func are not needed for writing an anonymous methods in C#, nor it is the only delegate type that be compiled to using expression trees. Having built-in types just made everything easier in .NET. And no, I dont allude that Action/Func are there for LINQ reason, I simply dont know.Wollongong
Again, I am just trying to help readers, but we seem to be decending into a fruitless argument. I should have been specific and described them as lambda expressions" not anonymous functions: delegate(string s) { Console.WriteLine(s); } was possible in C# 2.0. When I spoke of anonymous (closure) methods, I was referring to the C# language feature, not the IL implementation nor support for it. I'm sure it was possible to compile C# anonymous functions with delegates before. Lambda functions and LINQ were both introduced in C# version 3.Olli
@Todd Fruitless maybe but I too want to make it clear that Action/Func aren't the foundations of lambda expressions. Latter could be used with any delegate type. Action/Func just made anonymous methods more accessible/easier to use.Wollongong
LINQ was released along with C# version 3. The connection is objectively there. One does not use the delegate(type param) { body } syntax. The C# language treats them as Action<>/Func<>. This is a very important point. Kindly provide an example where I may be mistaken. Action/Func just made anonymous methods more accessible/easier to use - I disagree, the Async/Func syntax is embedded in the C# language, before C# version 3, this syntax didn't exist. While it's possible a delegate anon syntax could have been used, it wasn't. I'm describing what is reality, not what could have been.Olli
@Todd One does not use the delegate(type param) { body } - There is no objective connection as new List<string>().Any(delegate (string param) { return true; }) is equally valid syntax.Wollongong
Async/Func syntax is embedded in the C# language. I think you are mistaking the connection between a delegate type like Func like and syntax for expressing an anonymous function. No, Func syntax isn't embedded in the language. There is no syntax for Func in C#. Func is nothing special, it is just another type declared in BCL, for sort of enabling structural typing (for functions). You could write Func<int, string> d = delegate(int param) { return ""; }; as well as MyCustomDelegate = param => ""; Or you are using wrong terminology to convey.Wollongong
I stick to my initial point, Action/Func are neither the foundations of lambda expressions nor anonymous methods. The word foundation has a strong connotation. I would say Action/Func were good helpers to have, just made the whole closure thing damn easier.Wollongong
1) When do you see delegate (string param) { return true; }) in the usual LINQ documentation? They use (param) => { return true; } which is a Func<int, bool>, or (param) => true which is also Func<int, bool>. 2) No it is C#, founded on BCL and IL: (param) => {return true;} is C# syntax which compiles to Func<int,bool> which inherits from delegate. 3) "neither foundations of lambda" - I was referring to LINQ not the broader lambda expression capability. They both come out in the same language, and LINQ examples use the () => {} structure not the delgate one. Therefore: correlated.Olli
Let us continue this discussion in chat.Olli
C
66

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.

Culmiferous answered 19/12, 2010 at 11:2 Comment(1)
The Func<T1,T2,U> has a name like Func<T1,T2,T3,U> so should the compiler not say: Can not convert from Func1Name to Func2Name then it would NOT be less helpful.Talton
E
11

As some answers mention the win is clarity, you name the type and so it will be easier to understand for a user of your api. I'd say - in most cases - declare delegate types for your public apis but it's quite alright to use Func<?,?> internally.

One huge benefit of declaring the delegate type that is not mentioned in the other answers is that aside from giving the type a name you actually get to name the parameters also, this will massively increase the usability.

Embarkation answered 19/12, 2010 at 11:10 Comment(0)
A
10

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.

Ambrosio answered 19/12, 2010 at 11:6 Comment(0)
B
7

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> ?

Blowtorch answered 5/12, 2014 at 3:56 Comment(0)
S
4

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?"

Stopper answered 19/12, 2010 at 11:7 Comment(0)
R
3

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.

Rann answered 20/10, 2016 at 8:4 Comment(0)
A
1

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 = a => "ttt";
//----------
// Long Version Anonymous Function
Func<string, string> fLong = delegate(string a)
{
  return "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!!
Animato answered 18/8, 2016 at 10:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.