formatc
formatc

Reputation: 4323

When are named arguments useful?

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);

Upvotes: 37

Views: 20646

Answers (12)

Marc Wittmann
Marc Wittmann

Reputation: 2671

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)};

Upvotes: 2

Sklivvz
Sklivvz

Reputation: 31163

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);

Upvotes: 75

CJBS
CJBS

Reputation: 15685

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.

Upvotes: 1

Alexander
Alexander

Reputation: 20234

  • 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...

Upvotes: 2

Theraot
Theraot

Reputation: 40315

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).

Upvotes: 2

knut
knut

Reputation: 709

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.

Upvotes: 0

stuartd
stuartd

Reputation: 73293

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.

Upvotes: 9

Fede
Fede

Reputation: 44048

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.

Upvotes: 24

chwarr
chwarr

Reputation: 7202

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.

Upvotes: 24

Tony Hopkinson
Tony Hopkinson

Reputation: 20320

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.

Upvotes: 0

Bruno Costa
Bruno Costa

Reputation: 2720

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.

Upvotes: 6

thekaveman
thekaveman

Reputation: 4409

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);

Upvotes: 3

Related Questions