Pergunta

Uma coisa agradável sobre métodos anônimos é que eu posso usar variáveis ??que são locais no contexto de chamada. Existe alguma razão para que isso não funciona para fora-parâmetros e resultados de função?

function ReturnTwoStrings (out Str1 : String) : String;
begin
  ExecuteProcedure (procedure
                    begin
                      Str1 := 'First String';
                      Result := 'Second String';
                    end);
end;

exemplo muito artificial é claro, mas eu corri em algumas situações em que isso teria sido útil.

Quando tento compilar este, o compilador reclama que ele "não pode capturar símbolos". Além disso, eu tenho um erro interno uma vez quando eu tentei fazer isso.

Editar Eu só percebi que ele funciona para parâmetros normais como

... (List : TList)
Não

Isso é tão problemática quanto os outros casos? Quem garante que a referência ainda está apontando para um objeto vivo sempre que o método anônimo é executado?

Foi útil?

Solução

Var e parâmetros de saída ea variável resultado não pode ser capturada porque a segurança desta operação não pode ser estaticamente verificado. Quando a variável de resultado é de um tipo de gestão, tal como uma cadeia ou uma interface, o armazenamento é efectivamente reservada pelo chamador e uma referência para este armazenamento é transmitido como um parâmetro implícito; em outras palavras, a variável resultado, dependendo do seu tipo, é apenas como um parâmetro de saída.

A segurança não pode ser verificada pela razão Jon mencionado. O fecho criado por um método anónimo pode sobreviver a activação método em que foi criado, e pode sobreviver semelhante a activação do método que chamado o método em que foi criado. Assim, qualquer var ou fora parâmetros ou variáveis ??de resultados capturados pode acabar órfão, e quaisquer gravações para eles de dentro do fechamento no futuro iria corromper a pilha.

Claro, Delphi não é executado em um ambiente gerenciado, e ele não tem as mesmas restrições de segurança como, por exemplo C #. A linguagem poderia deixá-lo fazer o que quiser. No entanto, isso resultaria em difícil de diagnosticar erros em situações em que deu errado. O mau comportamento se manifestaria como variáveis ??locais em um valor de mudança de rotina sem causa próxima visível; seria ainda pior se o método de referência foram chamados de outro segmento.

Isso seria bastante difícil para depurar. breakpoints de memória hardware mesmo seria uma ferramenta relativamente pobre, como a pilha é modificada com freqüência. Seria necessário para ligar os pontos de interrupção de memória hardware sob condição de bater um outro ponto de interrupção (por exemplo, com a entrada método). O depurador Delphi pode fazer isso, mas eu arriscaria um palpite de que a maioria das pessoas não sabe sobre a técnica.

Atualizar : Com relação às adições à sua pergunta, a semântica de referências passageiras exemplo por valor é um pouco diferente entre os métodos que contêm um fecho (e capturar os paramete0 e métodos que não contêm . um fechamento de método ou pode reter uma referência ao argumento passado por valor;. métodos não captura o parâmetro pode simplesmente adicionar a referência a uma lista, ou armazená-lo em um campo particular

A situação é diferente com parâmetros passados ??por referência porque as expectativas do chamador são diferentes. Um programador fazer isso:

procedure GetSomeString(out s: string);
// ...
GetSomeString(s);

seria extremamente surpreendido se GetSomeString eram para manter uma referência para a variável s aprovada em Por outro lado:.

procedure AddObject(obj: TObject);
// ...
AddObject(TObject.Create);

Não é de surpreender que AddObject mantém uma referência, uma vez que o próprio nome indica que ele está adicionando o parâmetro para alguma loja stateful. Se a loja com informações de estado é sob a forma de um fecho ou não é um detalhe de implementação do método AddObject.

Outras dicas

O problema é que a variável Str1 não é "propriedade" de ReturnTwoStrings, para que seu método anônimo não pode capturá-lo.

A razão não pode capturá-lo, é que o compilador não sabe o proprietário final (em algum lugar na pilha de chamadas em direção chamando ReturnTwoStrings) para que ele não pode determinar onde para capturá-lo partir.

Editar: (Adicionado após um comentário de Smasher )

O núcleo de métodos anônimos é que eles capturam as variáveis ??(e não os seus valores).

Allen Bauer (CodeGear) explica um pouco mais sobre captura variável em seu blog .

Há um C # questão cerca de contornar o problema também.

O parâmetro de saída e valor de retorno são irrelevantes após o retorno da função - como é que você espera que o método anônimo se comportar se você capturou e executou-lo mais tarde? (Em particular, se você usar o método anônimo para criar um delegado, mas nunca executá-lo, o parâmetro de saída eo valor de retorno não seria definido pelo tempo que a função retornou.)

parâmetros

Fora são particularmente difíceis - a variável que os aliases de parâmetro para fora não pode mesmo existir pelo tempo que você mais tarde chamar o delegado. Por exemplo, suponha que você fosse capaz de capturar o parâmetro de saída e retorno do método anônimo, mas o parâmetro de saída é uma variável local na função de chamada, e é na pilha. Se o método chamar em seguida, retornou depois de armazenar a algum lugar delegado (ou devolvê-lo) o que aconteceria quando o delegado foi finalmente chamado? Onde é que ele escreva para quando o valor do parâmetro de saída foi definido?

Estou colocando isso em uma resposta em separado porque o seu EDIT faz a sua pergunta realmente diferente.

Eu provavelmente vou estender essa resposta mais tarde como eu estou em um pouco de pressa para chegar a um cliente.

A sua edição indica que você precisa repensar sobre tipos de valor, tipos de referência e o efeito de var, para fora, const e nenhuma marcação em todos os parâmetros.

Vamos fazer a coisa tipos de valor em primeiro lugar.

Os valores dos tipos de valor viver na pilha e têm um comportamento copy-on-atribuição. (Vou tentar incluir um exemplo sobre isso mais tarde).

Quando você não tem parâmetro de marcação, o valor real passado para um método (procedimento ou função) serão copiados para o valor local de que o parâmetro dentro do método. Assim, o método não funciona no valor passado para ele, mas em uma cópia.

Quando você tem para fora, var ou const, então nenhuma cópia ocorre: o método irá se referir ao valor real passado. Para var, que permitirá a alteração que valor real, para const não vai permitir isso. Por fora, você não será capaz de ler o valor real, mas ainda assim ser capaz de escrever o valor real.

Os valores de tipos de referência vivem na pilha, de modo que para eles pouco importa se você tem para fora, var, const ou nenhuma marcação parâmetro:. Quando você mudar alguma coisa, você alterar o valor na pilha

Para tipos de referência, você ainda obter uma cópia quando você não tem parâmetro de marcação, mas que é uma cópia de uma referência que ainda aponta para o valor na pilha.

Este é o lugar onde métodos anônimos ficar complicada: eles fazem uma captura variável. (Barry provavelmente pode explicar isso melhor ainda, mas vou dar-lhe uma tentativa) No seu caso editado, o método anônimo irá capturar a cópia local da lista. O método anônimo funcionará em que a cópia local, e de uma perspectiva compilador tudo é dandy.

No entanto, o ponto crucial de sua edição é a combinação de 'funciona para os parâmetros normais' e 'quem garante que a referência ainda está apontando para um objeto vivo sempre que o método anônimo é executado'.

Isso é sempre um problema com parâmetros de referência, não importa se você usar métodos anônimos ou não.

Por exemplo esta:

procedure TMyClass.AddObject(Value: TObject);
begin
  FValue := Value;
end;

procedure TMyClass.DoSomething();
begin
  ShowMessage(FValue.ToString());
end;

Quem garante que, quando alguém chama DoSomething, que a instância onde os pontos valor F para ainda existe? A resposta é que você deve garantir isso sozinho por não chamar DoSomething quando a instância de valor F morreu. O mesmo vale para a sua edição:. Você não deve chamar o método anônimo quando a instância subjacente morreu

Esta é uma das áreas onde a referência contados ou soluções de lixo coletado tornar a vida mais fácil: (! Que pode causar exemplo de viver mais tempo do que inicialmente previsto), há a instância será mantido vivo até a última referência a ele foi embora .

Assim, com a sua edição, a sua pergunta realmente muda a partir de métodos anônimos para as implicações do uso de parâmetros de referência digitado e gestão de vida em geral.

Esperamos que ajuda a minha resposta vai nessa área.

- jeroen

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top