36

Is there any case in C# code where positional arguments are not enough? I really don't see any benefit of named arguments, on the 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);

than:

private void Foo(bar: 1, baz: true);
Marcos Dimitrio
  • 6,651
  • 5
  • 38
  • 62
formatc
  • 4,261
  • 7
  • 43
  • 81
  • 2
    [MSDN](http://msdn.microsoft.com/en-us/library/dd264739.aspx) has a good example. – Tim Schmelter Dec 15 '13 at 23:07
  • 11
    there useful because your naming your values, rather than doSomething(true) which has no meaning for what true is, whats true??? with named params, this is clearly stated – davethecoder Dec 15 '13 at 23:10
  • 17
    `Foo`,`bar` and `baz` are poor names if you are talking about the benefits of named Arguments ;) – Tim Schmelter Dec 15 '13 at 23:13
  • 1
    To answer what means `1` and `true` in `private void Foo(1, true);` we need to check the documentation and/or implementation. In high quality code, everyting must obvious without documentation and comments. Names parameters is one of tools to reach this, however until it is optional the programmers WILL skip it becasue human brain prefers instant benefit like quck typing to the detriment of future bigger benefit like maintainable code. – Takeshi Tokugawa YD Mar 05 '22 at 03:32

12 Answers12

71

Named arguments are meant to increase readability. For example I've just used one as such

public void MarkAsDone(bool skipped) {}

Now by invoking the method without the name we have an ambiguity

MarkAsDone(true); //does true mean that it is successfully done?

Which can be resolved by clarifying with a name

MarkAsDone(skipped: true);

I think using the named parameter makes the client code way less ambiguous.

Apart from that they can be used to uniquely identify an optional parameter when there's more than one with the same type

MarkAsDone(int first, int second=0, int third=0) {}

///

MarkAsDone(1, third: 3);
kjhughes
  • 106,133
  • 27
  • 181
  • 240
Sklivvz
  • 30,601
  • 24
  • 116
  • 172
  • 10
    .. although in this example, having two methods `MarkAsDone` and `MarkAsSkipped` removes the possibility of ambiguity. – stuartd Dec 15 '13 at 23:12
  • 6
    @stuartd it introduces a different ambiguity, however it *is* smaller. Of course, this presumes you can change the called method, which is often not the case. – Sklivvz Dec 15 '13 at 23:13
  • @Sklivvz Nice example, although stuartd has a good point, considering performance hit, if any – formatc Dec 15 '13 at 23:18
  • 2
    Are named arguments maintenance friendly though? What happens if the parameter name is changed on the method without updating the caller? Does this add more overhead to method signatures, especially in APIs? – Adam E. Apr 06 '20 at 13:49
23

We found a very interesting use for named arguments when we needed to use a method like this:

private void ShowPopup(FrameworkElement content, 
                         string title = "My Application", 
                         bool isDialog = true,
                         double? width = null, 
                         double? height = null, 
                         double? offsetX = null, 
                         double? offsetY = null, 
                         bool isTransparent = false,
                         ... etc) 

where almost all parameters are optional. There are situations where you will want to leave all these parameters to their default, except one or a couple of them, such as:

PopupHelper.ShowPopup(_view, isTransparent: true);

or things like that.

Federico Berasategui
  • 43,562
  • 11
  • 100
  • 154
23

I use named parameters to make call sites clearer and when I have parameters with default values. The default values case has been discussed in a number of different answers already, so let's talk about call site clarity.

An analysis with metasyntactic variables isn't going to highlight their usefulness. Consider, instead this more "real-world", if you will, example.

Let's look at a call site:

something.OpenFile(documentPath, true);

What is this going to do? It's going to open documentPath. And do something else? What else? I can't remember, even though I wrote OpenFile only a week ago.

Here are three different examples for OpenFile that are relatively realistic.

void OpenFile(string path, bool shouldOverwrite)
void OpenFile(string path, bool preserveExisting)
void OpenFile(string path, bool enableWriting)

With named parameters, we can make the call sites clear:

something.OpenFile(documentPath, shouldOverwrite: false);

It's pretty clear that the file will not be overwritten.

something.OpenFile(documentPath, preserveExisting: false);

It's pretty clear that the file will be overwritten if needed.

And finally, we have:

something.OpenFile(documentPath, enableWriting: false)

It's pretty clear that the file will be opened for reading only.

Could this particular example be solved with something else like an enum? Yes. Can you always change the code? No. Does everyone else have the same abiding hatred for bool parameters that I do? No. :-)

Can you over do it with named parameters? Yes. Do good local variable names help? Tremendously.

chwarr
  • 6,777
  • 1
  • 30
  • 57
  • 3
    +1 for real world example, I can see benefit. Looks addictive :) But IMO, I would put enum here, feels safer somehow. – formatc Dec 15 '13 at 23:24
  • Isn't this what XML documentation is for? – Sinjai Aug 11 '17 at 19:47
  • The XML documentation is not typically rendered when looking at a place where the method _is being called_. And, if you're using a tool other than an IDE (like looking at a diff when reviewing a colleague's change), you probably can't quickly look at the XML documentation. – chwarr Aug 11 '17 at 21:01
9

They are useful - indeed implicitly required - when calling methods with optional parameters - because when you call a method of with optional parameters you must specify the ones you want to pass, otherwise you have to provide the whole list up to the last one you want to use.

Given a method like this:

public void Do(Thing thing, bool doItTwice = false, bool logResults = false,
               string something = null, bool makeTeaAfterwards = false)

You then must use named parameters to avoid having to specify the whole list:

Do(thing, makeTeaAfterwards: true);

Rather than:

Do(thing, false, false, null, true);

The latter also has the disadvantage that you must replicate the defaults, which introduces the possibility of error.

stuartd
  • 70,509
  • 14
  • 132
  • 163
6

I'm not sure, but I think you have misunderstood named parameters.

Please see: http://www.dotnetperls.com/named-parameters

Basically, they are useful when you've a lot of parameters to send to a method. With named parameters you can be sure of which parameters you are sending to the method

Method1(test1: 1, ..., test20: 10);

You should use it careful, as it has a substantial performance drawback.

Bruno Costa
  • 2,708
  • 2
  • 17
  • 25
  • Can you summarize what you think is useful from your link so that when it goes down or is moved, your answer stands on its own? – chwarr Dec 15 '13 at 23:08
  • Really? I told him he misunderstood the concept of named parameters and I give him useful information where he could find a good source to learn it. If that is not answering the question, please show me what is. – Bruno Costa Dec 15 '13 at 23:10
  • 1
    @c45207, thank you for your feedback. I suggest you to take some time to do some benchmark around named parameters. And if I was you I would do it with .net 4.0 and .net 4.5 to see the difference. With 4.5 is really better, but still have some performance issue. – Bruno Costa Dec 15 '13 at 23:24
  • 7
    The tests in the posted link - http://www.dotnetperls.com/named-parameters - do show a significant performance hit from using named parameters – stuartd Dec 15 '13 at 23:28
  • 3
    @stuartd I won't take dotnetperls values seriously. It has been a while since I last read the .NET EULA, but the version I remember said that if you publish performance benchmarks you must tell on what CPU, OS and version number of .NET it was. dotnetperls doesn't seem to do that... So I have done it with the code presented in dotnetperls on my machine (Windows 8.1 x86, Intel core i5 3.2 GHz x64, .NET 4.0), my results are 5.85 ns [for named parameters] vs 5.45 ns. Note: compiled with optimizations on. – Theraot Dec 15 '13 at 23:47
  • 1
    Yup. Thanks, all, for the links. I was really confused by the linked example until I realized that it was naming the parameters in an order different from their positions. In this case, due to, I assume, the required left-to-right evaluation order of method arguments, extra MSIL statements are added. If named parameters are used in the same order as their positions, which is what I originally tested, the compiler (C# 5.0 12.0.21005.1) can generate identical MSIL for positional and named parameters. – chwarr Dec 15 '13 at 23:49
  • Sorry, I should told you that. Btw, amazing answer ;) – Bruno Costa Dec 15 '13 at 23:52
  • 1
    @c45207 ah, left to rigth! I did test it, and I can confirm left to right evaluation order is taken into account. So another use of named parameters is as a shorthand to control the evaluation order of the parameters. That may have some side effects with sync and lazy evaluation, although I cannot think of case where it may be useful to exploit that. For me named parameters was just a thing for COM interop... good learning! – Theraot Dec 16 '13 at 00:02
  • 1
    My time machine ship crashed into this SO thread after plotting a course towards planet "named parameters" of the C# galaxy. I just wanted to add that dotnetperls updated their page to say the following: "Result: In 2019, no difference was found between the 2 calling syntax forms. Named parameters are fast." The result was 0.28 ns for both.... Take care. By the way, in 7 years, we're going to be hit by a nasty coronavirus. Stay healthy and wash your hands! – Adam E. Apr 06 '20 at 13:46
3

If you had a method signature like:

private void Foo(int bar, bool baz);

then named arguments don't help much, no.

But imagine a method signature like:

private void Foo(bool bar, int baz=0, int qux=0);

And say you wanted to pass the default value of baz but a parameter for qux, then named arguments helps there:

Foo(true, qux:1);
kaveman
  • 4,339
  • 25
  • 44
2

These days C# supports optional parameters, for example:

public void Dance(string song = "makarena",
                  string location = "your house",
                  string performer = "Michael",
                  DateTime? date = null,
                  int milliseconds = 0,
                  Action callback = null)
{
    ///party code
}

Now you can call it by skipping over some of the arguments (in any order):

Dance(location : "my house", date : DateTime.Now, performer : "Christina");

I tested the code. Sadly I didn't see Christina's sexy dance because I forgot to set the milliseconds parameter :P (Not my fault, but of those who did the API, why would they make milliseconds optional? :P).

My opinion is that the true value of this is in COM Interop and similar situations. For example Office COM objects has some methods with lots of arguments that are a pain without these (For example Word.Documents.Open).

Wreigh
  • 3,215
  • 16
  • 37
Theraot
  • 31,890
  • 5
  • 57
  • 86
2

Answered to a similar question that got deleted so I´ll post it here.. I think the Com Call argument has not been included yet:

There is one or two good reasons to use named parameters.

1) When using Com calls / Optional parameters

Imagine this:

var excelApp = new Microsoft.Office.Interop.Excel.Application();
excelApp.Workbooks.Add();
excelApp.Visible = true;
var myFormat =
Microsoft.Office.Interop.Excel.XlRangeAutoFormat.xlRangeAutoFormatAccounting1;

excelApp.get_Range("A1", "B4").AutoFormat(myFormat, Type.Missing, 
Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);

In C# 3.0 and earlier versions, you need to supply an argument for every parameter.

However, you can greatly simplify the call to AutoFormat by using named and optional arguments, introduced in C# 4.0.

excelApp.Range["A1", "B4"].AutoFormat( Format: myFormat );

2) Named parameters can create better / clearer code

File.Copy("source.txt", "destination.txt", true);

Unless you’re a developer that is already familiar with this method, you can only guess as to what the last Boolean parameter is for. With C# 4.0 named parameters this code can be written like this to express intent more clearly:

File.Copy("source.txt", "destination.txt", overwrite: true);

3) It shortens code

instead of using

Person person = new Person();
person.FirstName = "John";
person.LastName = "Smith";
person.DateOfBirth = new DateTime(1970, 1, 1);

you can use (depends on readability preferences)

Person person = new Person() { FirstName = "John", LastName="Smith", DateOfBirth = new DateTime(1970, 1, 1)};
Turbcool
  • 84
  • 8
Marc Wittmann
  • 2,286
  • 2
  • 28
  • 41
  • 3
    The third example is **not** about named arguments for a method call; it is an "Object Initializer" - a way to set up object **properties** along with constructor. – Alexandre Nov 06 '17 at 19:01
2
  • 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...

Alexander
  • 19,906
  • 19
  • 75
  • 162
1

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.

CJBS
  • 15,147
  • 6
  • 86
  • 135
0

They can be a nice declaration of intent, and increase readability where the arguments are the same type or can be implicitly converted.

E.g

int Duration(d1,d2)

Is that from d1 to d2 or d2 - d1 ? Intellisense might tell you if the arguments have good names, and or the documentation is correct and up to date. Or you could look at the code...

With multiple optional arguments they are even more useful, avoid rules like all optional arguments have to be the last ones, and all up to the one you want to not use the default have to be specified. Total nightmare if for some reason you need to re-factor the argument list. You might want to think about the differences between succinct and terse. Succinct is always good, terse rarely is.

Tony Hopkinson
  • 20,172
  • 3
  • 31
  • 39
0

I feel it it sacrifices compactness for readability. Lets say you have a function that cleans an array of some unwanted elements. Unwanted may be: old, deprecated, unused:

// unnamed:
collection.remove(true, true, false)
// named:
collection.remove(old:true, deprecated:true, unused:false);

There are many other ways to achieve this of course, from bitwise flags to intellisense. But, when programming in Python I use named params quite allot. Of course, no strong typing there, and worse tooling.

knut
  • 689
  • 5
  • 13