O acesso ao Encerramento modificado
Pergunta
string [] files = new string[2];
files[0] = "ThinkFarAhead.Example.Settings.Configuration_Local.xml";
files[1] = "ThinkFarAhead.Example.Settings.Configuration_Global.xml";
//Resharper complains this is an "access to modified closure"
for (int i = 0; i < files.Length; i++ )
{
// Resharper disable AccessToModifiedClosure
if(Array.Exists(Assembly.GetExecutingAssembly().GetManifestResourceNames(),
delegate(string name) { return name.Equals(files[i]); }))
return Assembly.GetExecutingAssembly().GetManifestResourceStream(files[i]);
// ReSharper restore AccessToModifiedClosure
}
A descrição acima parece funcionar bem, embora ReSharper reclama que este é "o acesso ao encerramento modificado". Qualquer um pode lançar luz sobre isso?
(este tema continuou aqui )
Solução
Neste caso, não há problema, desde que você está realmente executar o delegado em do loop.
Se você estava salvando o delegado e usá-lo mais tarde, no entanto, você acharia que todos os delegados iria lançar exceções ao tentar acessar arquivos [i] - eles estão capturando o variável i
em vez de seu valor no momento da criação delegados.
Em suma, é algo para estar ciente de como um potencial armadilha, mas neste caso ele não machucá-lo.
Veja o fundo desta página para um exemplo mais complexo, onde os resultados são contra-intuitivo.
Outras dicas
Eu sei que isto é uma questão de idade, mas eu estive recentemente estudando fechamentos e achei um exemplo de código pode ser útil. Nos bastidores, o compilador está gerando uma classe que representa um fechamento lexical para sua chamada de função. Provavelmente é algo como:
private sealed class Closure
{
public string[] files;
public int i;
public bool YourAnonymousMethod(string name)
{
return name.Equals(this.files[this.i]);
}
}
Como mencionado acima, a função funciona porque os predicados são invocados imediatamente após a criação. O compilador irá gerar algo como:
private string Works()
{
var closure = new Closure();
closure.files = new string[3];
closure.files[0] = "notfoo";
closure.files[1] = "bar";
closure.files[2] = "notbaz";
var arrayToSearch = new string[] { "foo", "bar", "baz" };
//this works, because the predicates are being executed during the loop
for (closure.i = 0; closure.i < closure.files.Length; closure.i++)
{
if (Array.Exists(arrayToSearch, closure.YourAnonymousMethod))
return closure.files[closure.i];
}
return null;
}
Por outro lado, se você fosse para armazenar e depois invocar o predicados, você veria que cada chamada para os predicados seria realmente chamar o mesmo método na mesma instância da classe encerramento e, portanto, seria usar o mesmo valor para i.
"arquivos" é um capturado variável externa porque ele foi capturado pela função de delegado anônimo. Sua vida útil é estendida pela função de delegado anônimo.
variáveis ??externas Captured Quando uma variável externa é referenciado por uma função anónimo, a variável exterior é dito ter sido capturado pela função anónimo. Normalmente, o tempo de vida de uma variável local é limitado a execução do bloco ou declaração com o qual está associado (variáveis ??locais). No entanto, o tempo de vida de uma variável externa capturado é estendida pelo menos até que a árvore delegado ou expressão criada a partir da função anônima torna-se elegível para coleta de lixo.
Quando uma variável local ou um parâmetro valor é capturado por uma função anônima, a variável local ou parâmetro não é mais considerado como uma variável fixa (fixo e variáveis ??móveis), mas em vez disso é considerado uma variável móvel. Assim, qualquer código inseguro que leva o endereço de uma variável externa capturado deve primeiro usar a instrução fixo para corrigir a variável. Note-se que ao contrário de uma variável uncaptured, uma variável local capturada pode ser simultaneamente exposto a vários threads de execução.