Creating delegates manually vs using Action/Func delegates

17,767

Solution 1

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.

Solution 2

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.

Solution 3

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.

Solution 4

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.

Solution 5

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

Share:
17,767
Elisabeth
Author by

Elisabeth

I am a develoBee summ summ :P

Updated on June 17, 2022

Comments

  • Elisabeth
    Elisabeth almost 2 years

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

  • Elisabeth
    Elisabeth over 13 years
    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.
  • Marwie
    Marwie over 9 years
    Here's another one where you cannot use Func - you cannot use Func if it has to return itself as mentionned here: stackoverflow.com/questions/27989296/…
  • nawfal
    nawfal over 9 years
    thanks @Marwie. Helpful. Will add to my answer at some point.
  • Jalal
    Jalal almost 8 years
    In example #5 params must be last parameter.
  • nawfal
    nawfal almost 8 years
    @Jalal you're right. Silly mistake. Anyone can edit answers anytime :)
  • Kind Contributor
    Kind Contributor almost 6 years
    You alluded to this with "LINQ". I believe Action/Func, are the foundation of anonymous (closure) methods.
  • nawfal
    nawfal almost 6 years
    @Todd I dont get you tbh. My answer isnt about LINQ.
  • Kind Contributor
    Kind Contributor almost 6 years
    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#.
  • nawfal
    nawfal almost 6 years
    @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.
  • Kind Contributor
    Kind Contributor almost 6 years
    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.
  • nawfal
    nawfal almost 6 years
    @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.
  • Kind Contributor
    Kind Contributor almost 6 years
    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.
  • nawfal
    nawfal almost 6 years
    @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.
  • nawfal
    nawfal almost 6 years
    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.
  • nawfal
    nawfal almost 6 years
    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.
  • Kind Contributor
    Kind Contributor almost 6 years
    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.
  • Kind Contributor
    Kind Contributor almost 6 years