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:
Que se passe-t-il si se font4 = new Font()
lance après que la ressource non gérée a été allouée par le constructeur mais avant que le ctor ne retourne et remplisse font4
la référence?
Permettez-moi de clarifier cela un peu plus. Supposons que nous ayons:
public sealed class Foo : IDisposable
{
private int handle = 0;
private bool disposed = false;
public Foo()
{
Blah1();
int x = AllocateResource();
Blah2();
this.handle = x;
Blah3();
}
~Foo()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!this.disposed)
{
if (this.handle != 0)
DeallocateResource(this.handle);
this.handle = 0;
this.disposed = true;
}
}
}
Maintenant nous avons
using(Foo foo = new Foo())
Whatever(foo);
C'est la même chose que
{
Foo foo = new Foo();
try
{
Whatever(foo);
}
finally
{
IDisposable d = foo as IDisposable;
if (d != null)
d.Dispose();
}
}
D'ACCORD. Supposons des Whatever
lancers. Ensuite, le finally
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 il foo
n'est jamais renseigné. Nous n'avons jamais entré le try
donc nous n'entrons jamais le finally
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 le finally
, 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 le handle
remplissage! Encore une fois, le finaliseur fonctionnera mais maintenant il handle
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:
bool lockEntered = false;
object lockObject = whatever;
try
{
Monitor.Enter(lockObject, ref lockEntered);
lock body here
}
finally
{
if (lockEntered) Monitor.Exit(lockObject);
}
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:
public Foo()
{
Blah1();
AllocateResource(ref handle);
Blah2();
Blah3();
}
et écrivez AllocateResource
intelligemment Monitor.Enter
pour que quoi qu'il se passe à l'intérieur AllocateResource
, le handle
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.