Proposal: Introduce delegate<T1, T2, …, TResult> as native function-type syntax in C# #9816
Replies: 4 comments 1 reply
-
Beta Was this translation helpful? Give feedback.
-
|
I think that the delegate design and other related features are historical baggage and should be phased out as soon as possible. We should directly emulate the function pointers introduced in .NET 5. If delegate*<T1...Tn> exists, just follow the pattern. delegate<T1...Tn> should be used without hesitation; there shouldn't be so many concerns. Those historically proven inefficient and ineffective features in C# should be replaced as soon as possible. I want to emphasize that this is of great significance. Combined with tail recursion, existing C# support for lambda expressions, pattern matching, and switch expressions, this reform will immediately transform C# into a full-fledged FP paradigm language. Theoretically, business logic can be built entirely on expressions without writing any imperative statements. This is a crucial paradigm because it allows C# to better perform parallelization, thanks to FP's inherent concurrency friendliness! This is very important. Combined with C#'s well-established OOP paradigm and unsafe capabilities, a single language will possess the capabilities of all the most advanced languages currently available, which is extremely useful. I want to emphasize that unsafe-clike, OOP, and FP are all very important and indispensable. |
Beta Was this translation helpful? Give feedback.
-
|
Delegates are completely meaningless, to the point that their current use is quite comical. The system has created so many versions of Fun Actions defined through delegates, which is very ugly. This reflects the fact that the definition of a delegate is entirely redundant and useless. Defining another delegate using |
Beta Was this translation helpful? Give feedback.
-
|
This not a C# language concern. .NET type system doesn't support this at all. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Problem Statement
Currently, C# uses Func<T1, T2, …, TResult> and Action<T1, T2, …> from the framework library to represent function types (delegates). This design has several limitations:
Func<> and Action<> are library-types, not language-native function types, which leads to inconsistency in syntax and semantics.
The keyword delegate is only used to define new delegate types (e.g., public delegate int MyDelegate(int x);), and not for generic function-type variables.
Func<> / Action<> incur the overhead of managed delegate objects, which may be unnecessary in scenarios where no capturing, no multicasting, and no runtime-invocation list behavior is needed.
The lack of a native function-type syntax makes it harder to unify with delegate*<…> (function pointer) concept, and makes the language’s function-value semantics less expressive.
Func<> / Action<> are heavily used in LINQ, expressions, APIs, which means a new design needs to consider existing ecosystem compatibility.
Proposal
Introduce a new, language-level generic function type syntax using the delegate keyword. For example:
delegate<T1, T2, TResult> // a function taking T1, T2 returning TResult
delegate<T1, T2, T3, void> // a function taking T1, T2, T3 with no return value
delegate // a function taking no parameters returning int
Rules:
The last generic type argument always denotes the return type.
If the last argument is void, the function returns nothing (void).
Benefits:
Syntax consistency: using delegate aligns with existing delegate-type pattern.
Declarative and type-level: function types become first-class citizens.
Unified design: complements delegate*<…> (unsafe function pointers) and Func<> / Action<> can gradually be deprecated for new code.
Performance: for non-capturing/static methods the compiler can optimise calls as direct delegates or even inline calls, avoiding delegate-object overhead.
Use Cases / Scenarios
FP-style higher-order functions where function types are passed around.
Low-allocation, high-performance APIs (e.g., Span, pipelines) where delegate overhead is undesired.
API design requiring clear semantics of “function value” rather than “delegate object”.
Interoperation between safe delegates and unsafe function pointers (delegate*<…>).
Impact on existing code & compatibility
Backwards compatibility: Func<> / Action<> remain supported; the new syntax is additive.
Existing delegate definitions (e.g., public delegate int MyDel(int x);) are unaffected.
Over time APIs can adopt delegate<T…> in new libraries while legacy APIs continue to use Func<> / Action<>.
The compiler and tooling will need support for the new syntax, function-type variable declarations, type inference, lambdas targeting this new type, etc.
Example
// declare a variable of new function type:
delegate<int, int, int> adder = (x, y) => x + y;
// supporting void return:
delegate<string, void> printer = s => Console.WriteLine(s);
int result = adder(3, 4); // 7
printer("hello"); // prints hello
Risks and considerations
Requires compiler changes to treat delegate<T…> as a function type (not just a type alias).
Expression trees, reflection, serialization might need updates to support the new type.
There is a large ecosystem of libraries expecting Func<>, so migration or coexistence needs careful planning.
Tooling (IntelliSense, analyzers, code fixers) must be updated to recognize the new syntax.
Need to define how lambdas, method groups, inference, overload resolution work with delegate<T…>.
Conclusion
Adding the delegate<T1…Tn> function type syntax would enhance C#’s consistency, strengthen its FP-capabilities, unify delegate/function semantics, and enable higher-performance scenarios. I believe this proposal aligns with C#’s evolution toward more expressive, type-safe, and performance-oriented language features.
Beta Was this translation helpful? Give feedback.
All reactions