Non-trailing named arguments in C# 7.2

One small change that comes with C# 7.2 is support for non-trailing named arguments in method calls. This post explains what are non-trailing named arguments, how to use them and how they look after compiling.

Source code available! Those who want some basic samples of new features can take my solution from GitHub repository gpeipman/CSharp7. This is (almost) the same solution I’m using for presentations.

Let’s look at simple method that calculates volume of rectangular prism.

public int Volume(int a, int b, int c)
    return a * b * c;

Ńamed arguments before C# 7.2

In previous versions of C# we had two ways how to use named arguments. We can specify all argument names and leave out those with default value or we specify positional arguments first and then named arguments.

v = Volume(a: 3, c: 5, b: 4);
v = Volume(3, b: 4, c: 5);

Positional arguments were not allowed after named ones before.

Non-trailing named arguments

With C# 7.2 it is now allowed to have named arguments also after positional ones. This feature is called non-trailing named arguments. In C# 7.2 we can also write a call like this.

v = Volume(3, b: 4, 5);

There are still some rules to follow and we cannot randomly mix up argument lists. Here is the example of call that is not allowed.

Method call above messes up the order of arguments. For C# compiler we have specified argument c twice – first time when specifying it as a named argument and second time by having non-named argument on position of argument c. This is not allowed and all possible intrepretations for this situation probably end up with hard to read and non-intuitive code.

Named arguments after compiling

Let’s make my favorite trick and build sample code. Before opening it in disassembler I remove pdb file to make sure that disassembler cannot find out anything about language features I used. All calls given above (except the one that doesn’t compile) are shown here. This is what we get after compiling.

internal class Program
    private static void Main(string[] args)
        Program.Volume(3, 4, 5);
        Program.Volume(3, 4, 5);
        Program.Volume(3, 4, 5);

    public static int Volume(int a, int b, int c)
        return a * b * c;

Named arguments – be it trailing or non-trailing – are language feature and it has nothing to do with Common Language Runtime (CLR). They exist only in C# but not in framework itself.

Wrapping up

Support for non-trailing named arguments is new language feature of C# 7.2. It’s small but nice addition that, in some cases, helps us write more readable code. On .NET Framework level it changes nothing, as we saw. C# compiler translates all method calls with named arguments to regular method calls and CLR has no idea how the call was originally written.

Liked this post? Empower your friends by sharing it!
Categories: C#

View Comments (3)

  • I fail to understand the usefulness of it. It's either you use named arguments or you don't. Why bother mixing up named and positional arguments if you have to have them in the right position anyway?

  • One case that comes to my mind is method with default values and long argument names. This is the case when non-trailing named arguments can be useful.

  • I can see it also being useful for bool arguments, to clarify what that value means in that context. What's clearer:

    Foo(reverse: true);

    If there were more arguments following 'reverse', older versions of C# would require them to be named as well, even if they were perfectly clear from context. For example:

    var frobnicator = new Frobnicator ();
    Bar(false, frobnicator);
    Bar(reverse: false, frobnicator: frobnicator);

    In the new version you can just write:

    Bar(reverse: false, frobnicator);

    Traditional advice was to replace the bool parameter with an enumeration:

    enum Direction

    Bar(Direction.Reverse, frobnicator);

    but that can be a lot of additional code if you have many methods with bool parameters which don't overlap in meaning. You end up with many enumerations that are used in very few places.

    You also get naming problems if, say, Baz needs Left and Right options - you end up calling the enums BarDirection and BazDirection to avoid the name clash, meaning the calls start becoming less clear again.

Related Post