<p>Delegates have been part of the design of C# for decades. But would it be wrong to say that over time, all the uses of delegates have become better achieved through other means?</p>
<p>(Or, dramatically speaking, have delegates become the now-irrelevant remains of the historical first steps towards adding first-class functions support to C#?)</p>
<ul>
<li><p>The delegate syntax looks like it is just a more verbose alternative to <code>=&gt;</code>.</p>
</li>
<li><p>The delegate type declarations look like they are just an alias/typedef for <code>Func&lt;&gt;</code> or <code>Action&lt;&gt;</code>? (They do let us document the parameter names, though.)</p>
</li>
<li><p>C# events are traditionally declared using delegate types. But when binding a handler to an event with <code>+=</code>, the conversion of methods (or lambdas) to delegates is automatic anyway. Wouldn't regular <code>Func</code>/<code>Action</code> be sufficient?</p>
</li>
</ul>
<p>Given all that, I wonder if someone discovering C# in 2024 would have any reason to bother even learning about delegates.</p>
<p>In fact, it feels like delegates could be removed from the language entirely (if only backwards compatibility wasn't the eternal bane of non-esoteric language design) without losing anything. Am I missing anything?</p>

<language-design><c#>
Are delegates still relevant in modern C#?
CC BY-SA 4.0
2024-02-26T10:47:45.727Z
Have you considered that all those "other means" might just be syntactic sugar and will compile down to a delegate anyway?
ratchet freak
CC BY-SA 4.0
2024-02-26T22:20:56.167Z
Welp. Looks like they do. I find that a bit counterintuitive and sad, because the other means are easier to use. Is it only me?
Eldritch Conundrum
<p>Well, I don't think you can remove <em>delegates</em> from the language since delegates are what allows you to pass methods and closures around. <code>System.Func</code> and <code>System.Action</code> are also just delegate types and not some kind of compiler magic. They don't cover all possible method signatures though. For example, to store/accept a method with a <code>ref</code>/<code>out</code>/<code>in</code>/<code>params</code>/<code>scoped</code> parameter, a parameter with a default value, or a parameter with attributes, you still need a delegate type declaration – there is no <code>Func&lt;in int, out int, params int[]&gt;</code> syntax.</p>
<p>Note that delegates are heap-allocated objects, unlike <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/unsafe-code#function-pointers" rel="noreferrer">function pointers</a>, which use the <code>delegate*&lt;T1, T2&gt;</code> syntax.</p>
<p>You are right that <em>anonymous method expressions</em> (<code>delegate (int a, int b) { return a &lt; b; }</code>) are usually not very useful as they can be replaced with a lambda expression most of the time. There are a few edge-cases though:</p>
<ol>
<li><p><em>anonymous method expression</em> allows omitting the parameter list – this has no exact equivalent (you'd have to use discards) in the lambda expression syntax:</p>
<pre class="lang-cs prettyprint-override"><code>Func&lt;int, int, int, int, int, int, int&gt; f = delegate { return 42; };
Func&lt;int, int, int, int, int, int, int&gt; f = (_, _, _, _, _, _) =&gt; 42;
</code></pre>
</li>
<li><p>only lambda expression can be converted into LINQ <code>Expression&lt;T&gt;</code>, which can influence overload resolution:</p>
<pre class="lang-cs prettyprint-override"><code>void Transform(Func&lt;int, int&gt; function)
    =&gt; Console.WriteLine(&quot;f(42) = &quot; + function(42));
void Transform(Expression&lt;Func&lt;int, int&gt;&gt; expr)
    =&gt; Console.WriteLine(&quot;f = &quot; + expr);

Transform(x =&gt; x * 8) // error CS0121: The call is ambiguous [...]
Transform(delegate (int x) { return x * 8; }) // calls the plain Func overload
</code></pre>
</li>
</ol>

CC BY-SA 4.0
2024-02-25T13:45:17.570Z
2024-02-25T13:45:17.570Z
Ghost4Man
CC BY-SA 4.0
2024-02-26T02:27:34.597Z
ref, out, params, default values, and attributes... Wow, I didn't notice that so many things were not expressible with Func/Action.
Eldritch Conundrum
CC BY-SA 4.0
2024-02-26T02:33:58.587Z
Ok, it is now clearer that delegates are the fundamental concept. Lambdas and Func/Action are just sugar on top, to facilitate the most common cases. (And they facilitate so well that I didn't even realize how much delegates are needed.)
Eldritch Conundrum
<p>You're conflating several different ideas here under the moniker of &quot;delegate&quot;, and that's mostly our fault for over-using the term in the first place. Let's break it apart into several different questions:</p>
<ul>
<li>Is there anything you can do with the C# 2 <em>anonymous method</em> syntax that you cannot also do with the C# 3 <em>lambda</em> syntax?</li>
</ul>
<p>For the most part, no. There are some corner cases that I won't go into here because they are rare in practice.</p>
<p>I think it is fair to say that the C# 2 team regrets the &quot;heavyweight&quot; choice of syntax for anonymous methods in C# 2, and that we all agree that it would have been better to go with the lighter-weight lambda syntax in the first place. It's unfortunate that we're stuck with two very different-looking ways to do the same thing, but it can't be removed now for backwards-compatibility reasons.</p>
<ul>
<li>Is there anything you can do with a <em>delegate type declaration</em> that you cannot do with <code>Func&lt;&gt;</code> or <code>Action&lt;&gt;</code> ?</li>
</ul>
<p>Yes. Two things come to mind.</p>
<p>First, <code>Func&lt;&gt;</code> and <code>Action&lt;&gt;</code> do not allow you to describe type signatures with <code>in</code>, <code>out</code> or <code>ref</code> parameters. You'll still need a custom delegate syntax for those.</p>
<p>C# will now infer delegate types and you can see this in action:</p>
<pre><code>var a = (int y) =&gt; y;
Console.WriteLine(a.GetType().Name);
var b = (ref int y) =&gt; y + 1;
Console.WriteLine(b.GetType().Name); 
</code></pre>
<p>Prints</p>
<pre><code>Func`2
&lt;&gt;F{00000001}`2
</code></pre>
<p>The first is inferred to <code>Func&lt;int, int&gt;</code>. The second generates a new delegate type with an &quot;unspeakable&quot; name because there is no such thing as <code>Func&lt;ref int, int&gt;</code>.</p>
<p>Second, having your own custom delegates lets you more clearly document the purpose of the delegate. In the original design of delegates, the .NET team believed that delegate types with semantics would be created; I want a delegate that is a predicate, or that is a pure function, or that has certain security semantics, or whatever. That didn't come to pass.</p>
<p>Given what we know now, it would possibly have been better to have structural typing on delegates -- type by signature -- rather than have delegates typed by name.</p>
<p>Also, don't forget that generics weren't added until C# 2. Any solution that requires generics in the type system would be a non-starter for C# 1.</p>
<ul>
<li>Events are a thin wrapper around a list of delegates. Do we really need the concepts of both &quot;event&quot; and &quot;delegate&quot;?</li>
</ul>
<p>It's helpful to understand what .NET was a reaction to.</p>
<p>COM's bestiary of outgoing interfaces and IConnectionPointContainer and whatnot takes some getting used to. It's complicated, it's arguably over-engineered, and don't we really just need a publish-subscribe model for callbacks?</p>
<p>The idea was that in .NET we could massively simplify the system for the user while at the same time maintaining the nice property that the &quot;outgoing&quot; interface of an object is represented in its metadata by clearly tagging the outgoing members with &quot;event&quot;.  Just having a property of delegate type doesn't make it clear to tooling that the <em>purpose</em> of the thing is a callback.</p>
<p>I've long thought that there was room for streamlining the event handling mechanisms even more to make them less like thin wrappers around delegates, but there's not much pressure on designers to work on this area when there are lots of other bigger-bang-for-buck possibilities.</p>

CC BY-SA 4.0
2024-02-27T00:24:59.577Z
2024-02-27T00:24:59.577Z
Eric Lippert
CC BY-SA 4.0
2024-03-02T00:31:39.207Z
Good point about C# 1 not having generics.  I wonder how many other C# language features would have been done differently if generics had been around from the beginning.
dan04
CC BY-SA 4.0
2024-03-02T05:57:51.960Z
@dan04: A great many! C# 1.0 has several kinds of "generics" but they are all one-off special cases. In a world with generics, plainly there is no need for `T[]`. There is `Array<T>`.  Sure, efficiently packed arrays are fundamental and would still need to have special support in the implementation, but there's no need for them to be so special in the type system the way they are today.
Eric Lippert
CC BY-SA 4.0
2024-03-02T06:00:50.490Z
It's also instructive to ponder what if .NET had had a standard mechanism for tuples in the first place, instead of having to watch them be re-invented in inconsistent ways half a dozen times. Just now I wrote some code that needs to distinguish between a function that has parameter signature (int, int) and one that has ((int, int)), OH THE PAIN.
Eric Lippert
# Are delegates still relevant in modern C#?
Posted by: username_0 on 2024-02-24
Delegates have been part of the design of C# for decades. But would it be wrong to say that over time, all the uses of delegates have become better achieved through other means?

(Or, dramatically speaking, have delegates become the now-irrelevant remains of the historical first steps towards adding first-class functions support to C#?)

* The delegate syntax looks like it is just a more verbose alternative to `=>`.
* The delegate type declarations look like they are just an alias/typedef for `Func<>` or `Action<>`? (They do let us document the parameter names, though.)
* C# events are traditionally declared using delegate types. But when binding a handler to an event with `+=`, the conversion of methods (or lambdas) to delegates is automatic anyway. Wouldn't regular `Func`/`Action` be sufficient?

Given all that, I wonder if someone discovering C# in 2024 would have any reason to bother even learning about delegates.

In fact, it feels like delegates could be removed from the language entirely (if only backwards compatibility wasn't the eternal bane of non-esoteric language design) without losing anything. Am I missing anything?
**Tags:** language-design c#
> **Comments:**
> **username_1**: Have you considered that all those "other means" might just be syntactic sugar and will compile down to a delegate anyway?  
> **username_0**: Welp. Looks like they do. I find that a bit counterintuitive and sad, because the other means are easier to use. Is it only me?  
---
## 2 answers
### Answer by username_3 on 2024-02-27. Score: 11
You're conflating several different ideas here under the moniker of "delegate", and that's mostly our fault for over-using the term in the first place. Let's break it apart into several different questions:

Is there anything you can do with the C# 2 anonymous method syntax that you cannot also do with the C# 3 lambda syntax?

For the most part, no. There are some corner cases that I won't go into here because they are rare in practice.
I think it is fair to say that the C# 2 team regrets the "heavyweight" choice of syntax for anonymous methods in C# 2, and that we all agree that it would have been better to go with the lighter-weight lambda syntax in the first place. It's unfortunate that we're stuck with two very different-looking ways to do the same thing, but it can't be removed now for backwards-compatibility reasons.

Is there anything you can do with a delegate type declaration that you cannot do with Func<> or Action<> ?

Yes. Two things come to mind.
First, Func<> and Action<> do not allow you to describe type signatures with in, out or ref parameters. You'll still need a custom delegate syntax for those.
C# will now infer delegate types and you can see this in action:
var a = (int y) => y;
Console.WriteLine(a.GetType().Name);
var b = (ref int y) => y + 1;
Console.WriteLine(b.GetType().Name); 

Prints
Func`2
<>F{00000001}`2

The first is inferred to Func. The second generates a new delegate type with an "unspeakable" name because there is no such thing as Func.
Second, having your own custom delegates lets you more clearly document the purpose of the delegate. In the original design of delegates, the .NET team believed that delegate types with semantics would be created; I want a delegate that is a predicate, or that is a pure function, or that has certain security semantics, or whatever. That didn't come to pass.
Given what we know now, it would possibly have been better to have structural typing on delegates -- type by signature -- rather than have delegates typed by name.
Also, don't forget that generics weren't added until C# 2. Any solution that requires generics in the type system would be a non-starter for C# 1.

Events are a thin wrapper around a list of delegates. Do we really need the concepts of both "event" and "delegate"?

It's helpful to understand what .NET was a reaction to.
COM's bestiary of outgoing interfaces and IConnectionPointContainer and whatnot takes some getting used to. It's complicated, it's arguably over-engineered, and don't we really just need a publish-subscribe model for callbacks?
The idea was that in .NET we could massively simplify the system for the user while at the same time maintaining the nice property that the "outgoing" interface of an object is represented in its metadata by clearly tagging the outgoing members with "event".  Just having a property of delegate type doesn't make it clear to tooling that the purpose of the thing is a callback.
I've long thought that there was room for streamlining the event handling mechanisms even more to make them less like thin wrappers around delegates, but there's not much pressure on designers to work on this area when there are lots of other bigger-bang-for-buck possibilities.
> **Comments:**
> **username_4** (2024-03-02): Good point about C# 1 not having generics.  I wonder how many other C# language features would have been done differently if generics had been around from the beginning.  
> **username_3** (2024-03-02): @dan04: A great many! C# 1.0 has several kinds of "generics" but they are all one-off special cases. In a world with generics, plainly there is no need for `T[]`. There is `Array<T>`.  Sure, efficiently packed arrays are fundamental and would still need to have special support in the implementation, but there's no need for them to be so special in the type system the way they are today.  
> **username_3** (2024-03-02): It's also instructive to ponder what if .NET had had a standard mechanism for tuples in the first place, instead of having to watch them be re-invented in inconsistent ways half a dozen times. Just now I wrote some code that needs to distinguish between a function that has parameter signature (int, int) and one that has ((int, int)), OH THE PAIN.  
---
### Answer by username_2 on 2024-02-25. Score: 6
Well, I don't think you can remove delegates from the language since delegates are what allows you to pass methods and closures around. System.Func and System.Action are also just delegate types and not some kind of compiler magic. They don't cover all possible method signatures though. For example, to store/accept a method with a ref/out/in/params/scoped parameter, a parameter with a default value, or a parameter with attributes, you still need a delegate type declaration – there is no Func syntax.
Note that delegates are heap-allocated objects, unlike function pointers, which use the delegate* syntax.
You are right that anonymous method expressions (delegate (int a, int b) { return a < b; }) are usually not very useful as they can be replaced with a lambda expression most of the time. There are a few edge-cases though:

anonymous method expression allows omitting the parameter list – this has no exact equivalent (you'd have to use discards) in the lambda expression syntax:
Func f = delegate { return 42; };
Func f = (_, _, _, _, _, _) => 42;


only lambda expression can be converted into LINQ Expression, which can influence overload resolution:
void Transform(Func function)
    => Console.WriteLine("f(42) = " + function(42));
void Transform(Expression> expr)
    => Console.WriteLine("f = " + expr);

Transform(x => x * 8) // error CS0121: The call is ambiguous [...]
Transform(delegate (int x) { return x * 8; }) // calls the plain Func overload
> **Comments:**
> **username_0** (2024-02-26): ref, out, params, default values, and attributes... Wow, I didn't notice that so many things were not expressible with Func/Action.  
> **username_0** (2024-02-26): Ok, it is now clearer that delegates are the fundamental concept. Lambdas and Func/Action are just sugar on top, to facilitate the most common cases. (And they facilitate so well that I didn't even realize how much delegates are needed.)  
---
