I would like to discuss a C# argument forwarding expression using ... inside invocation argument lists.
The goal is to make forwarding methods, local functions, and lambdas easier to write and safer to maintain by allowing the current callable’s parameters to be forwarded into another invocation.
static void Forward(int x, string y)
{
Target(...);
}
static void Target(int x, string y)
{
}The intent is that Target(...) behaves as though the parameters of Forward were written explicitly:
Target(x, y);Forwarding methods are common in wrapper APIs, overload funnels, logging/decorator layers, adapter methods, local functions, and lambdas.
Today, forwarding requires manually repeating every parameter:
static void Forward(
int id,
string name,
CancellationToken cancellationToken)
{
Target(id, name, cancellationToken);
}This repetition is easy to get wrong when parameters are added, removed, reordered, or changed to ref, out, or in.
An argument forwarding expression would let the developer express intent directly:
static void Forward(
int id,
string name,
CancellationToken cancellationToken)
{
Target(...);
}For an initial version, ... would only be valid inside a normal invocation argument list.
Target(...);
Target(prefix, ...);
Target(..., suffix);When used, ... expands to the parameters of the containing callable in declaration order.
static void Forward(int x, string y)
{
Target(0, ...);
}would behave like:
Target(0, x, y);The forwarded arguments should participate in normal overload resolution and type inference.
The forwarding expression should preserve parameter passing mode.
static void Forward(ref int x, out string y, in DateTime z)
{
Target(...);
}
static void Target(ref int x, out string y, in DateTime z)
{
}This should behave like:
Target(ref x, out y, in z);For a conservative first version, these should be invalid:
Target(x: ...); // named forwarding
Target(ref ...); // explicit ref on forwarding expression
new C(...); // object creation
Target(..., ...); // multiple forwarding expressions in same argument list- Should
...forward all containing parameters, or only “remaining” parameters based on position? - Should explicit arguments after
...be allowed? - Should
...be allowed in constructors or object creation? - Should multiple
...expressions be disallowed, as proposed here? - Is
...the right syntax? - Should this feature support attributes, indexers, delegates, or function pointer invocations, or only ordinary method invocations?
- Should lambda/local-function forwarding be allowed?
I have a local Roslyn prototype that implements this experimentally.
The prototype includes:
DotDotDotTokenForwardArgumentsExpressionSyntax- parser support for
...inside argument lists - binder expansion before overload resolution
- preservation of
ref,out, andin - diagnostics for invalid contexts
- tests for:
- lexing/parsing
- basic forwarding
- overload resolution
- generic type inference
- optional parameters
paramsref,out, andin- local functions and lambdas
- invalid contexts
- duplicate forwarding
I am not submitting the implementation as a PR yet because I understand substantial language features should be discussed first. I would like feedback on whether this direction is worth pursuing, whether the syntax/semantics are appropriate, and what design constraints should be considered before any implementation PR.
Rosyln: https://github.com/dotnet/roslyn
This code branch is branched from the Roslyn main branch on 08/15/2026
CSharpLang: https://github.com/dotnet/csharplang
This is where the discussion is located for the language feature: dotnet/csharplang#10308
M src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs M src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs M src/Compilers/CSharp/Portable/CSharpResources.resx M src/Compilers/CSharp/Portable/Errors/ErrorCode.cs M src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs M src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4 M src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Internal.Generated.cs M src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Main.Generated.cs M src/Compilers/CSharp/Portable/Generated/CSharpSyntaxGenerator/CSharpSyntaxGenerator.SourceGenerator/Syntax.xml.Syntax.Generated.cs M src/Compilers/CSharp/Portable/Parser/LanguageParser.cs M src/Compilers/CSharp/Portable/Parser/Lexer.cs M src/Compilers/CSharp/Portable/Syntax/Syntax.xml M src/Compilers/CSharp/Portable/Syntax/SyntaxKind.cs M src/Compilers/CSharp/Portable/Syntax/SyntaxKindFacts.cs M src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf M src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf M src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf M src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf M src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf M src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf M src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf M src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf M src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf M src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf M src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf M src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf M src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf M src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenCallTests.cs M src/Compilers/CSharp/Test/Syntax/Generated/Syntax.Test.xml.Generated.cs M src/Compilers/CSharp/Test/Syntax/LexicalAndXml/LexicalTests.cs M src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs