When are named arguments useful?

Viewed 17610

Is there any case in C# code where positional arguments are not enough? I really don't see any benefit of named arguments, on contrary I can see how overusing of named arguments could make code hard to read? So my question is, why would someone use them and how can it help in writing better code as I'm sure they were not implemented without reason?

This looks cleaner to me:

private void Foo(1, true);

then:

private void Foo(bar: 1, baz: true);
12 Answers
  • Good practice is to keep the arguments in the correct order still.
  • Good practice is to not blindly remove the names when you get your readability from elsewhere.

Example function:

public void TrackDataChange(IEntity oldData, IEntity newData)

Old call:

dataTrackerService.TrackDataChange(newData: valuesFromClient.ToEntity(), oldData: valuesFromDb.ToEntity())

New call:

var oldData = new ValueEntityConverter(valuesFromDb).Entity;
var newData = new ValueEntityConverter(valuesFromClient).Entity;

dataTrackerService.TrackDataChange(newData, oldData);

Of course this compiles but the resulting data is now messed up, because

  • originally the order was wrong but still worked correctly because of the names
  • someone removed the names but didn't check the order

Not sure you can solely blame either developer...

Useful to ensure non-breaking code when calling against generated methods

In an application where a method has parameter values that are tied to fields in a database (e.g. a constructor for a test objects that has properties in the database), it's possible that, based on a change to the database, the order of the parameters might change. If the data types of the method parameters remain the same, and hand-rolled code (e.g. a unit test) calls the generated method, it can be very difficult to see that the hand-rolled code no longer matches the generated code. Named parameters can help prevent this.

For example:

A database has a table MyCreation with columns IsBig and IsColoured. Using an ORM, a test method to create sample data exists to populate such data:

/* Generated Code from an ORM */
public IMyCreation CreateTestObject(bool isBig, bool isColoured)
{
    IMyCreation _myCreation = new MyCreation();
    _myCreation.IsBig = isBig;
    _myCreation.IsColoured = isColoured;

    return _myCreation;
}

A method in a hand-rolled test class makes use of this:

var myCreation = mcTest.CreateTestObject(false, true);

Now, if the DB were to change, e.g. a parameter were renamed (IsBig becomes IsGrande), then the order of the parameters might change, and the generated function now becomes:

/* Generated Code from an ORM */
public IMyCreation CreateTestObject(bool isColoured, bool isGrande)
{
    IMyCreation _myCreation = new MyCreation();
    _myCreation.IsColoured = isColoured;
    _myCreation.IsGrande = isGrande;

    return _myCreation;
}

However, everything will still compile. The calling code is still valid, but no longer correct, as the value for each parameter is different.

var myCreation = mcTest.CreateTestObject(false, true);

If named parameters are used, protection against generated parameter changes is achieved:

var myCreation = mcTest.CreateTestObject(isBig: false, isColoured: true);

... this code would then break (in the case of a param rename) - which is desirable!


Or, in the case of a simple parameter swap without a rename, would continue to work without needing a fix:

var myCreation = mcTest.CreateTestObject(isBig: false, isColoured: true);

would be correct, regardless of whether the function signature is

public IMyCreation CreateTestObject(bool isBig, bool isColoured)

or

public IMyCreation CreateTestObject(bool isColoured, bool isBig)

For generated code, where ugly code is more tolerable, an approach like this might be useful to force named parameters to be used.

Related