Creating Delegates Manually VS Using Action/Func Delegates

Creating delegates manually vs using Action/Func delegates

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.

Delegates vs Action, Func in C#

None of the Func or Action types allow out or ref parameters, so you'll have to define your own delegates if you need to use those e.g.:

public delegate bool TryParse<T>(string s, out T value);

What is the difference between using a delegate and using FuncT/ActionT in a method signature?

Custom delegate types vs Func and Action

Why use Func and/or Action when you can achieve the same results with a delegate?

Because:

  • It saves you the trouble of creating a custom delegate type for each possible method signature. In code, less is more.
  • Different custom delegate types are incompatible, even if their signatures exactly match. You can work around this but it's verbose.
  • Since the introduction of Func and Action this is the idiomatic way to write code. Unless there is compelling reason for the opposite, you want to do as the Romans do.

Let's see what the problem is:

// Delegates: same signature but different types
public delegate void Foo();
public delegate void Bar();

// Consumer function -- note it accepts a Foo
public void Consumer(Foo f) {}

Trying it out:

Consumer(new Foo(delegate() {})); // works fine
Consumer(new Bar(delegate() {})); // error: cannot convert "Bar" to "Foo"

The last line is problematic: there is no technical reason why it cannot work, but the compiler treats Foo and Bar as the distinct types they are and disallows it. This can lead to friction because if all you have is a Bar you would have to write

var bar = new Bar(delegate() {});
Consumer(new Foo(bar)); // OK, but the ritual isn't a positive experience

Why use a delegate over Func and/or Action?

Because:

  • You are targeting an early version of C# where these types do not exist.
  • You are working with complicated function signatures. Noone would want to type this more than once: Func<List<Dictionary<int, string>>, IEnumerable<IEnumerable<int>>>.

Since I consider both of these as rare occurrences, in everyday usage the practical answer is "no reason at all".

Composing multicast delegates

All delegates in C# are multicast delegates -- that is, invoking them can potentially invoke any number of methods with that signature. The operators + and - do not perform function composition; they add and remove a delegate from a multicast delegate. An example:

void Foo() {}
void Bar() {}

var a = new Action(Foo) + Bar;
a(); // calls both Foo() and Bar()

You can remove a delegate from a multicast delegate with operator-, but you must pass the exact same delegate in. If right-hand-side operand was not already part of the multicast delegate then nothing happens. For example:

var a = new Action(Foo);
a(); // calls Foo()
a -= Bar; // Bar is not a part of the multicast delegate; nothing happens
a(); // still calls Foo() as before

Multicast delegate return values

Invoking a multicast delegate with a non-void return type results in the value returned by the last added member of the multicast delegate. For example:

public int Ret1() { return 1; }
public int Ret2() { return 2; }

Console.WriteLine((new Func<int>(Ret1) + Ret2)()); // prints "2"
Console.WriteLine((new Func<int>(Ret2) + Ret1)()); // prints "1"

This is documented in the C# spec (§15.4, "delegate invocation"):

Invocation of a delegate instance whose invocation list contains
multiple entries proceeds by invoking each of the methods in the
invocation list, synchronously, in order. Each method so called is
passed the same set of arguments as was given to the delegate
instance. If such a delegate invocation includes reference parameters
(§10.6.1.2), each method invocation will occur with a reference to the
same variable; changes to that variable by one method in the
invocation list will be visible to methods further down the invocation
list. If the delegate invocation includes output parameters or a
return value, their final value will come from the invocation of the
last delegate in the list
.

Aside: "Cannot assign method group to an implicitly-typed local variable"

First of all you need to know what a method group is. The specification says:

A method group, which is a set of overloaded methods resulting from a
member lookup (§7.4). [...] A method group is
permitted in an invocation-expression (§7.6.5), a
delegate-creation-expression (§7.6.10.5) and as the left hand side of
an is operator, and can be implicitly converted to a compatible
delegate type (§6.6). In any other context, an expression classified
as a method group causes a compile-time error.

So, given a class with these two methods:

public bool IsInteresting(int i) { return i != 0; }
public bool IsInteresting(string s) { return s != ""; }

When the token IsInteresting appears in the source, it's a method group (note that a method group can of course consist of one single method, as in your example).

The compile-time error is expected (the spec mandates it) because you are not trying to convert it to a compatible delegate type. Being more explicit solves the problem:

// both of these convert the method group to the "obviously correct" delegate
Func<int, bool> f1 = IsInteresting;
Func<string, bool> f2 = IsInteresting;

In layman's terms it's not meaningful to write var f = IsInteresting because the only reasonable thing for the compiler would be to create a delegate, but it does not know which method it should point to.

In the special case where the method group contains exactly one method this problem is solvable. Off the top of my head I can think of two reasons why the C# team did not allow it to work:

  1. Consistency is good.
  2. Would lead to breakage of perfectly good code if another overload is introduced later. Introducing a compile error to code that calls IsInteresting(int) because you added an IsInteresting(string) would leave a really bad impression.

Problem with Action/Func delegates in VS 2019

The problem is in this line:

removeMax(numbers.ToList());

This call: numbers.ToList() creates a new list from numbers array so when removeMax is called it removes an element from a list which is a copy of array numbers not the numbers array itself as it was probably intended.

When should we use ActionT and not create a custom delegate

Well...

Action<T> is almost the same as delegate void (T t)
and
Func<T> is almost the same as delegate T ()

Action and Func (and lambdas) are just 'syntactical sugar' and a convenience for using delegates.

So it's really just a matter of preference.

When are delegates and Func/Action not interchangeable?

As Jon Skeet answered in the comments, I was missing the fact that Action/Func are literally delegates (I thought it was classes encapsulating delegates) :

namespace System
{
public delegate TResult Func<in T, out TResult>(T arg);
}

So there's no difference between delegates and corresponding Func/Action because Func/Action are delegates.

use Func (or Action) or create own delegate?

Func<> is useful when it's very clear what they're used for, and the number of inputs is small.

When the number of inputs is larger, or there could be some ambiguity over the intent - then using a delegate with named arguments makes things clearer.

C# Action, Func , Can be Nested or Chaining?

Delegates are simpler than you think.

Let's take Func.The last argument is the result that must be returned.

So you need to specify at least one generic parameter

For examaple Func<int,string> can point do any methods that accept an int and return a string
In the following example all 4 funcs do the same thing.

Q1:You can make them as complex as you want.

Q2:No Action is not the same a Func.It works because the is an overload that accepts a Func
Q3:It didn't work because without the (i) you don't actually call the method.You called opFactory and ignored its result

using System;
namespace ConsoleApp1
{

class Program
{
static void Main(string[] args)
{
///lamda statement
Func<int, string> func4 = i => { return i.ToString(); };

///lamda expression (if there is only 1 statement)
Func<int, string> func3 = i => i.ToString();

Func<int, string> func2 = delegate (int i) { return i.ToString(); };

//Just point to an existing method
Func<int, string> func1 = ToString;
}

static string ToString(int arg)
{
return arg.ToString();
}
}
}

And a full example

using System;
using System.Linq;
using System.Threading.Tasks;
namespace ConsoleApp1
{

class Program
{
static void Main(string[] args)
{
int Length = 5;

var arg1 = Enumerable.Range(10, Length).ToArray();
var arg2 = Enumerable.Range(100, Length).ToArray();

var expected = new int[Length];
var expected_for = new int[Length];
var expected_foreach = new int[Length];

Func<int[], Action<int>> opFactory = res => i => res[i] = arg1[i] + arg2[i];

Parallel.For(0, arg1.Length, opFactory(expected));

Enumerable.Range(0, arg1.Length).ToList().ForEach(opFactory(expected_foreach));

for (int i = 0; i < arg1.Length; i++)
{
opFactory(expected_for)(i);
}

//all 3 tables have the same data 110,112,114,116,118
Console.ReadLine();
}
}
}

Using Expression and Action delegate for decision making structure

Here is solution with actions and Func delegates, which do not use if...else structure. Actually this is a kind of Chain of Responsibility:

public class LicenseHandler
{
private readonly Func<GroupLicense, User, bool> predicate;
private readonly Action action;
private LicenseHandler nextHandler;

public LicenseHandler(Func<GroupLicense,User, bool> predicate, Action action)
{
this.action = action;
this.predicate = predicate;
}

public LicenseHandler SetNext(LicenseHandler handler)
{
nextHandler = handler;
return this;
}

public void Handle(GroupLicense license, User user)
{
if (predicate(license, user))
{
action();
return;
}

if (nextHandler != null)
nextHandler.Handle(license, user);
}
}

Create handlers and chain them:

var handler1 = new LicenseHandler((l, u) => l.IsEnabled && u.IsActive, Action1);
var handler2 = new LicenseHandler((l, u) => !l.IsEnabled && u.IsActive, Action2);
var handler3 = new LicenseHandler((l, u) => !l.IsEnabled && u.IsLocked, Action3);

handler1.SetNext(handler2.SetNext(handler3));

Processing looks like

handler1.Handle(licence, user);

People, who will support delegate-based code instead of simple if-else, accept my deepest sympathy.

UPDATE: If you want nice fluent API, then you can create builder class, which will create handlers chain:

public class LicenseHandlerBuilder
{
private LicenseHandler root;
private LicenseHandler current;

private LicenseHandlerBuilder() { }

public static LicenseHandlerBuilder CreateHandler(
Func<GroupLicense, User, bool> predicate, Action action)
{
var builder = new LicenseHandlerBuilder();
builder.root = new LicenseHandler(predicate, action);
builder.current = builder.root;
return builder;
}

public LicenseHandlerBuilder WithNext(
Func<GroupLicense, User, bool> predicate, Action action)
{
var next = new LicenseHandler(predicate, action);
current.SetNext(next);
current = next;
return this;
}

public LicenseHandler Build()
{
return root;
}
}

Usage:

var handler = LicenseHandlerBuilder
.CreateHandler((l, u) => l.IsEnabled && u.IsActive, Action1)
.WithNext((l, u) => !l.IsEnabled && u.IsActive, Action2)
.WithNext((l, u) => !l.IsEnabled && u.IsLocked, Action3)
.Build();


Related Topics



Leave a reply



Submit