简体   繁体   中英

What are the rules for named arguments and why?

Consider a method like this

void RegisterUser(string firstname, string lastname, int age);

I like explicitly naming the arguments of methods like this when I call them because it's easy for someone to mix up the firstname and lastname arguments. However, it's not really necessary for age . For instance, I would think this should be OK from a clarity standpoint.

RegisterUser(firstname: "John", lastname: "Smith", 25);

But the following error would be thrown:

Named argument specifications must appear after all fixed arguments have been specified

Another interesting thing is that if the signature were

void RegisterUser(int age, string firstname, string lastname);

then calling it as follows does NOT throw an error

RegisterUser(25, firstname: "John", lastname: "Smith");

Why is C# designed like this? Is there a complication for the compiler if the first scenario were allowed?

the compiler might be able to figure it out but for us mere humans it would be nearly impossible to know if 25 refers to the 1st or 3rd parameter. Especially since it opens up the posibility of mixing arguments. why not

MyFunction(firstname: "josh", 25, "smith", someotherargument: 42)

How would you interpret this, 25 for age and smith for lastname? make a rule for it and a compiler can implement it. But what would make sense to humans. Code obfuscation shouldn't be that easy

A language should make it hard to make errors, not easier

NOTE: strange things start happening with the ordering if earlier arguments are named later. (like the firstname & smith in my example) because then becomes a puzzle for your unnamed arguments to be mapped to the right arguments. it could be done, but code shouldn't produce puzzles

This is because when you name arguments the compiler maps it based on the names and ignores the function definition as long as all required arguments are present. In your case it doesn't know what 25 is. To us that seems logical that it has to be age, but if you change your example to:

void RegisterUser(string firstname, string lastname, int age = 0, int weight = 0);

and then say:

RegisterUser(firstname: "John", lastname: "Smith", 25);

Then the compiler doesn't know what to do with that last 25. This way of calling a function is mostly used in functions that has a lot of default values where you only want to set a few.

When not naming your arguments you're basically saying that you strictly follow the structure set by the function definition.

The intended use, with:

void M(int a = -1, int b = -1, int c = -1, int d = -1, int e = -1);

as an example, is that you can specify a subset of those optional parameters either by positional notation:

M(42, 28, 101);  // gives a, b, and c in order; omits d and e

or you can use named arguments:

M(d: 50, a: 42, c: 101);  // gives three arguments in arbitrary order

or you can combine them, where you start with positional arguments and then switch to naming arguments:

M(42, 28, e: 65537, d: 50);  // mixed notation OK

The reason for the restriction you have encountered, is that stuff like:

M(c: 101, 7, 9, b: 28, 666);  // ILLEGAL!

would be confusing.

I can see you propose to keep the positional ordering in your specific call, and then include names of some arguments only, for clarity. However, it appears that this use was not a priority for the language designers.

I suggest you name all arguments when your reason for using named style is clarity (as opposed to a need to specify a proper subset of the parameters only).

All named arguments have to come after positional arguments; you can't switch between the styles. Positional arguments always refer to the corresponding parameter in the method declaration. You can't make positional arguments skip a parameter by specifying it later with a named argument. The compiler uses temporary local variables. It then reorders those locals in the argument slots, my guess is that the compiler binds by arguments by order until it finds a named argument then it discards the arguments that it has already bound without names and reordered as the compiler uses temporary local variables. The binds the rest by name, for instance it binds 25 with age then reordered firstname: "John", lastname: "Smith"

Positional arguments are placed before named arguments. Positional arguments always refer to the corresponding parameter in the method declaration .

Suppose my method is:

void Dimensions(int height, int breadth , int length);

and i'm calling it like

Dimensions(3, length: 12, 24);

In this case:

'3' is the first parameter and refers to height, but '24' is third parameter and refers to length, but we have already specified value of length.

So maybe to overcome this hindrance, by default c# style is to position positional arguments at start to provide correct referencing.

Also if we are defining optional parameters, providing positional parameters at end may lead to wrong results.

I think the language design in this case drives on the first named argument of any function.

Using your example

void RegisterUser(int age, string firstname, string lastname);

and calling it

RegisterUser(25, firstname: "John", lastname: "Smith");

Before encountering the first argument the compiler doesn't know whether this function is going to use any named arguments at all. Therefore it's a safe assumption for the compiler to do sequential mapping.

25:

The compiler gets the first argument and if it is a non named one then it will be mapped with the first argument from the function definition straight away.

firstname:

As soon as it encounters it's first named argument, things will change as the compiler will now have to check all the remaining arguments mentioned in the function definition to map the current named argument.

After it successfully maps it's first named argument the trail of sequential mapping has been broken. And therefore it's not feasible to provide a non named argument to compiler now as now it cannot establish where to map it.

If you think it should remember the last non named argument and start sequential mapping from there then that's error prone too as the named argument just defined might be sequentially right as well.

lastname:

And for the named arguments it's a breeze.

Hope this helps :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM