65 votes

Resharper: fermeture implicitement capturée: cette

Je reçois cet avertissement ("Implicity capturé la fermeture: ceci") de Resharper: cela signifie-t-il que ce code capture en quelque sorte l'intégralité de l'objet englobant?

     internal Timer Timeout = new Timer
                            {
                                Enabled = false,
                                AutoReset = false
                            };
    public Task<Response> ResponseTask
    {
        get
        {
            var tcs = new TaskCompletionSource<Response>();

            Timeout.Elapsed += (e, a) => tcs.SetException(new TimeoutException("Timeout at " + a.SignalTime));

            if (_response != null) tcs.SetResult(_response);
            else ResponseHandler += r => tcs.SetResult(_response);
            return tcs.Task;
        }
    }
 

Je ne sais pas comment ni pourquoi cela se produit - la seule variable à capturer est la TaskCompletionSource, qui est intentionnelle. Est-ce réellement un problème et comment pourrais-je m'y prendre si c'est le cas?

EDIT: l'avertissement est sur le premier lambda (l'événement Timeout).

27voto

Aaron Maslen Points 526

Il semble que le problème n'est pas la ligne, je pense qu'il est.

Le problème est que j'ai deux lambdas référencement champs dans l'objet parent: Le compilateur génère une classe avec deux méthodes et une référence à la classe parent (this).

Je pense que ce serait un problème, parce que la référence à l' this pourrait potentiellement rester autour de la TaskCompletionSource objet, l'empêchant d'être GCed. Au moins, c'est ce que j'ai trouvé sur cette question suggère.

La classe générée ressemblerait à quelque chose comme ceci (bien évidemment des noms différents, et imprononçables):

class GeneratedClass {
    Request _this;
    TaskCompletionSource tcs;

    public lambda1 (Object e, ElapsedEventArgs a) {
        tcs.SetException(new TimeoutException("Timeout at " + a.SignalTime));
    }

    public lambda2 () {
        tcs.SetResult(_this._response);
    }
}

La raison pour laquelle le compilateur ne c'est sans doute de l'efficacité, je suppose, que l' TaskCompletionSource est utilisé par les deux lambdas, mais en tant qu'une référence à l'un de ces lambdas est encore référencée dans la référence à l' Request objet est également maintenue.

Je ne suis toujours pas plus avancés sur la façon d'éviter ce problème, cependant.

EDIT: évidemment je n'avais pas penser à ça quand j'étais à l'écrire. J'ai résolu le problème en changeant la méthode comme ceci:

    public Task<Response> TaskResponse
    {
        get
        {
            var tcs = new TaskCompletionSource<Response>();

            Timeout.Elapsed += (e, a) => tcs.SetException(new TimeoutException("Timeout at " + a.SignalTime));

            if (_response != null) tcs.SetResult(_response);
            else ResponseHandler += tcs.SetResult; //The event passes an object of type Response (derp) which is then assigned to the _response field.
            return tcs.Task;
        }
    }

13voto

SLaks Points 391154

Il semble que _response est un champ de votre classe.

Le référencement _response du lambda capturera this dans la fermeture et lira this._response lorsque le lambda sera exécuté.

Pour éviter cela, vous pouvez copier _response dans une variable locale et l'utiliser à la place. Notez que cela le conduira à utiliser la valeur actuelle de _response plutôt que sa valeur éventuelle.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X