Reading the Jon Skeet book, I have found (some time now) the use of the “Named arguments” in the function call. Here is a fast and easy example :
void Dump(int x, int y, int z, string cSomeText)
{
// no use, just to see how we call this later
string cOnMe = string.Format("[{0}] [{1}] [{2}] [{3}]", x, y, z, cSomeText);
}
void CallDumpWithoutNameArguments()
{
// call with out Name Arguments
Dump(1, 2, 3, "Test string");
}
void CallDumpWithNameArguments()
{
// more easy to read, call the same function with Name Arguments
Dump(x: 1, y: 2, z: 3, cSomeText: "Test string");
}
After using it and later see the compiled code I see that the use of this name actually create variables before the call of the function.
And this is the created code:
private void CallDumpWithoutNameArguments()
{
this.Dump(1, 2, 3, "Test string");
}
private void CallDumpWithNameArguments()
{
int CS$0$0000 = 1;
int CS$0$0001 = 2;
int CS$0$0002 = 3;
string CS$0$0003 = "Test string";
this.Dump(CS$0$0000, CS$0$0001, CS$0$0002, CS$0$0003);
}
and the full compiled code, you see how bigger is I call it using the “Named Arguments”
.method private hidebysig instance void CallDumpWithoutNameArguments()
{
.maxstack 8
nop
ldarg.0
ldc.i4.1
ldc.i4.2
ldc.i4.3
ldstr "Test string"
call instance void SubSonic.BabisExtrasNoUseIt.ExtraTestCode::Dump(int32 x, int32 y, int32 z, string cSomeText)
nop
ret
}
.method private hidebysig instance void CallDumpWithNameArguments()
{
.maxstack 5
.locals init (int32 V0,
int32 V1,
int32 V2,
string V3)
nop
ldarg.0
ldc.i4.1
stloc.0
ldc.i4.2
stloc.1
ldc.i4.3
stloc.2
ldstr "Test string"
stloc.3
ldloc.0
ldloc.1
ldloc.2
ldloc.3
call instance void SubSonic.BabisExtrasNoUseIt.ExtraTestCode::Dump(int32 x, int32 y, int32 z, string cSomeText)
nop
ret
}
So this is one point that c# have forget to optimize, or is there any other use of them ?
Follow up
I like to clear that the code above is what the compile produce. Here is what I get with the answer from Servy.
private void CallDumpWithoutNameArguments()
{
// what generated from
// int i = 0;
// Dump(i++, i++, i++, cSomeText: "Test string");
int i = 0;
string CS$0$0000 = "Test string";
this.Dump(i++, i++, i++, CS$0$0000);
}
private void CallDumpWithNameArguments()
{
// what is generate from
// int i = 0;
// Dump(x: i++, z: i++, y: i++, cSomeText: "Test string");
int i = 0;
int CS$0$0000 = i++;
int CS$0$0001 = i++;
int CS$0$0002 = i++;
string CS$0$0003 = "Test string";
this.Dump(CS$0$0000, CS$0$0002, CS$0$0001, CS$0$0003);
}
This has to do with ensuring that the code runs in the proper order. With named arguments the expressions for each argument need to be executed in the order they appear in the source code, not the order that they appear in the actual argument list of the definition. Imagine the (very mean) call:
should it end up the same as:
or
If you don’t have local variables for each named argument you end up with the first one, if you do, you end up with the second one.
It would appear that rather than trying to determine if it’s necessary to make local variables (are the arguments out of order, do they cause side effects, are those side effects visible to the expressions in other arguments) it was simpler to just always create the local variables. It has been stated on a number of occasions that the C# compiler will ensure correctness over optimizations; it leaves optimizations for the JIT.