C # me permet de faire ce qui suit (exemple de MSDN):
using (Font font3 = new Font("Arial", 10.0f),
font4 = new Font("Arial", 10.0f))
{
// Use font3 and font4.
}
Que se passe-t-il si font4 = new Font
jette? D'après ce que je comprends, font3 perdra des ressources et ne sera pas éliminé.
- Est-ce vrai? (font4 ne sera pas éliminé)
- Est-ce que ce moyen
using(... , ...)
doit être évité en faveur d'une utilisation imbriquée?
c#
using
using-statement
Benjamin Gruenbaum
la source
la source
using(... , ...)
est compilé en blocs à l'aide de blocs imbriqués, mais je ne le sais pas avec certitude.using
du tout, le GC finira par le récupérer.finally
bloc, il ne serait pas entré dans le bloc tant que toutes les ressources n'ont pas été construites.using
en atry
-finally
, l'expression d'initialisation est évaluée en dehors dutry
. C'est donc une question raisonnable.Réponses:
Non.
Le compilateur générera un
finally
bloc séparé pour chaque variable.La spécification (§8.13) dit:
la source
MISE À JOUR : J'ai utilisé cette question comme base pour un article qui peut être trouvé ici ; le voir pour une discussion supplémentaire sur cette question. Merci pour la bonne question!
Bien que la réponse de Schabse soit bien sûr correcte et réponde à la question qui a été posée, il existe une variante importante de votre question que vous n'avez pas posée:
Permettez-moi de clarifier cela un peu plus. Supposons que nous ayons:
Maintenant nous avons
C'est la même chose que
D'ACCORD. Supposons des
Whatever
lancers. Ensuite, lefinally
bloc s'exécute et la ressource est désallouée. Aucun problème.Supposons des
Blah1()
lancers. Ensuite, le lancer se produit avant que la ressource ne soit allouée. L'objet a été alloué mais le ctor ne revient jamais, donc ilfoo
n'est jamais renseigné. Nous n'avons jamais entré letry
donc nous n'entrons jamais lefinally
non plus. La référence d'objet est devenue orpheline. Finalement, le GC le découvrira et le mettra dans la file d'attente du finaliseur.handle
est toujours zéro, donc le finaliseur ne fait rien. Notez que le finaliseur doit être robuste face à un objet en cours de finalisation dont le constructeur n'est jamais terminé . Vous devez écrire finaliseurs qui sont ce fort. C'est encore une autre raison pour laquelle vous devriez laisser la rédaction des finaliseurs à des experts et ne pas essayer de le faire vous-même.Supposons des
Blah3()
lancers. Le lancer se produit après l'allocation de la ressource. Mais encore une fois,foo
n'est jamais renseigné, nous n'entrons jamais dans lefinally
, et l'objet est nettoyé par le thread de finalisation. Cette fois, la poignée est différente de zéro et le finaliseur la nettoie. Là encore, le finaliseur s'exécute sur un objet dont le constructeur n'a jamais réussi, mais le finaliseur s'exécute quand même. Evidemment il le faut car cette fois, il avait du travail à faire.Supposons maintenant des
Blah2()
lancers. Le lancer se produit après l'allocation de la ressource, mais avant lehandle
remplissage! Encore une fois, le finaliseur fonctionnera mais maintenant ilhandle
est toujours à zéro et nous perdons la poignée!Vous devez écrire un code extrêmement intelligent pour éviter que cette fuite ne se produise. Maintenant, dans le cas de votre
Font
ressource, qui s'en soucie? Nous perdons une poignée de police, gros problème. Mais si vous exigez absolument que toutes les ressources non gérées soient nettoyées, quel que soit le moment des exceptions, vous avez un problème très difficile entre les mains.Le CLR doit résoudre ce problème avec les verrous. Depuis C # 4, les verrous qui utilisent l'
lock
instruction ont été implémentés comme ceci:Enter
a été écrit très soigneusement afin que, quelles que soient les exceptions levées ,lockEntered
soit défini sur true si et seulement si le verrou a été effectivement pris. Si vous avez des exigences similaires, vous devez en fait écrire:et écrivez
AllocateResource
intelligemmentMonitor.Enter
pour que quoi qu'il se passe à l'intérieurAllocateResource
, lehandle
soit rempli si et seulement s'il doit être désalloué.Décrire les techniques pour le faire dépasse le cadre de cette réponse. Consultez un expert si vous avez cette exigence.
la source
Blah
appels de méthode. Qu'est-ce qui empêche une ThreadAbortException de se produire à l'un ou l'autre de ces points?AllocateResource
mais avant l'affectation àx
. UnThreadAbortException
peut se produire à ce stade. Tout le monde ici semble manquer mon point, qui est la création d'une ressource et l'attribution d'une référence à celle-ci à une variable n'est pas une opération atomique . Afin de résoudre le problème que j'ai identifié, vous devez en faire une opération atomique.En complément de la réponse @SLaks, voici l'IL de votre code:
Notez les blocs try / finally imbriqués.
la source
Ce code (basé sur l'exemple d'origine):
Il produit le CIL suivant (dans Visual Studio 2013 , ciblant .NET 4.5.1):
Comme vous pouvez le voir, le
try {}
blocage ne démarre qu'après la première allocation, qui a lieu àIL_0012
. À première vue, cela semble allouer le premier élément en code non protégé. Cependant, notez que le résultat est stocké à l'emplacement 0. Si la deuxième allocation échoue alors, le bloc externefinally {}
s'exécute, et cela récupère l'objet à l'emplacement 0, c'est-à-dire la première allocation defont3
, et appelle saDispose()
méthode.Fait intéressant, la décompilation de cet assemblage avec dotPeek produit la source reconstituée suivante:
Le code décompilé confirme que tout est correct et que le
using
est essentiellement développé enusing
s imbriqués . Le code CIL est un peu déroutant à regarder, et j'ai dû le regarder pendant quelques bonnes minutes avant de bien comprendre ce qui se passait, donc je ne suis pas surpris que des `` histoires de vieilles femmes '' aient commencé à germer à propos de ce. Cependant, le code généré est la vérité inattaquable.la source
Voici un exemple de code pour prouver la réponse @SLaks:
la source
font4 = new Font
jette? D'après ce que j'ai compris, font3 perdra des ressources et ne sera pas éliminé."