実際の .NET 実装とその背後にある決定に興味があります。
たとえば、Java では、匿名クラスで使用されるすべてのキャプチャ値が final である必要があります。この要件は、.NET では削除されたようです。
また、参照型とは対照的に、値型のキャプチャされた値の実装に違いはありますか?
ありがとう
それがどのように実装されているかを知る最も簡単な方法は、それを試すことです。キャプチャされた変数を使用するコードを記述し、コンパイルしてから、Reflectorで確認します。キャプチャされるのは値ではなく、変数であることに注意してください。これは、この分野でのJavaとC#の大きな違いの1つです。
基本的な考え方は、少なくとも1つのキャプチャされた変数を含むスコープの各レベルが、キャプチャされた変数のフィールドを持つ新しいクラスになるということです。複数のレベルがある場合、内部スコープには次のスコープ出力用のフィールドもあります。スタック上の本物のローカル変数は、自動生成されたクラスのインスタンスへの参照になります。
次に例を示します。
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<Action> actions = new List<Action>();
for (int i=0; i < 5; i++)
{
int copyOfI = i;
for (int j=0; j < 5; j++)
{
int copyOfJ = j;
actions.Add(delegate
{
Console.WriteLine("{0} {1}", copyOfI, copyOfJ);
});
}
}
foreach (Action action in actions)
{
action();
}
}
}
(もちろん、コピーを取らないと、異なる結果が得られます-実験してください!)これは、次のようなコードにコンパイルされます。
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<Action> actions = new List<Action>();
for (int i=0; i < 5; i++)
{
OuterScope outer = new OuterScope();
outer.copyOfI = i;
for (int j=0; j < 5; j++)
{
InnerScope inner = new InnerScope();
inner.outer = outer;
inner.copyOfJ = j;
actions.Add(inner.Action);
}
}
foreach (Action action in actions)
{
action();
}
}
class OuterScope
{
public int copyOfI;
}
class InnerScope
{
public int copyOfJ;
public OuterScope outer;
public void Action()
{
Console.WriteLine("{0} {1}", outer.copyOfI, copyOfJ);
}
}
}
キャプチャされた変数へのすべての参照は、生成されたクラスのインスタンスを通過することになります。したがって、これは1回限りのコピーではありません。(この場合、コード内の他の何もキャプチャされた変数を使用しませんが、それが可能であることは容易に想像できます。)外側のループの1回の反復で、5つの新しいインスタンスがすべての1つのインスタンスを共有する方法に注意してくださいOuterScope
。デリゲートで追加のコードを試して、それが物事にどのように影響するかを確認することをお勧めします。デリゲートが変更された場合、copyofI
その変更は次のデリゲートで確認されます。次のデリゲートはの別のインスタンスを使用するため、への変更は表示されcopyOfJ
ませんInnerScope
。