8

Jon Skeetの本を読んで、私は(しばらくして)関数呼び出しで「名前付き引数」が使用されていることを発見しました。これが速くて簡単な例です:

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

それを使用した後、コンパイルされたコードを見ると、この名前を使用すると、関数を呼び出す前に実際に変数が作成されることがわかります。

そして、これは作成されたコードです:

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

完全にコンパイルされたコードを見ると、「名前付き引数」を使用して呼び出すことがどれほど大きいかがわかります。

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

それで、これはc#が最適化するのを忘れた1つのポイントです、またはそれらの他の使用法はありますか?

ファローアップ

上記のコードがコンパイルによって生成されるものであることを明確にしたいと思います。これが私が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);
}
4

1 に答える 1

8

これは、コードが適切な順序で実行されるようにすることと関係があります。名前付き引数では、各引数の式は、定義の実引数リストに現れる順序ではなく、ソース コードに現れる順序で実行する必要があります。(非常に意地悪な)呼び出しを想像してください:

int i = 0;
Dump(x: i++,  z: i++, y: i++, cSomeText: "Test string");

それは次のようになります:

Dump(0, 1, 2, "Test string");

また

Dump(0, 2, 1, "Test string");

名前付き引数ごとにローカル変数がない場合は、最初の引数が使用されます。ある場合は、2 番目の引数が使用されます。

ローカル変数を作成する必要があるかどうか (引数の順序が間違っているか、副作用を引き起こしているか、それらの副作用が他の引数の式に表示されているか) を判断しようとするよりも、常にローカル変数。C# コンパイラは最適化よりも正確性を保証すると何度も述べてきました。JIT の最適化を残します。

于 2012-09-11T19:31:26.397 に答える