Mise à JOUR: j'ai utilisé cette question comme base pour un article qui peut être trouvé ici; voir pour une discussion approfondie de cette question. Merci pour la bonne cause!
Si Schabse la réponse est, bien sûr, correct et répond à la question qui a été posée, il y a une variante importante sur votre question, vous n'avez pas demander:
Ce qui se passe si font4 = new Font()
jette après le non géré les ressources allouées par le constructeur, mais avant de le ctor retourne et remplit font4
avec la référence?
Permettez-moi de rendre cela un peu plus clair. Supposons que l'on ait:
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 le même que
{
Foo foo = new Foo();
try
{
Whatever(foo);
}
finally
{
IDisposable d = foo as IDisposable;
if (d != null)
d.Dispose();
}
}
OK. Supposons Whatever
lancers. Puis l' finally
bloc s'exécute, et la ressource est libéré. Pas de problème.
Supposons Blah1()
lancers. Alors le jet qui se passe avant que la ressource est allouée. L'objet a été alloué, mais le ctor ne retourne jamais, alors foo
n'est jamais rempli. Nous ne nous sommes jamais entrés dans l' try
, nous n'avons jamais entrer dans l' finally
. La référence d'objet a été orphelin. Finalement, le GC va découvrir cela et le mettre sur le finaliseur file d'attente. handle
est toujours égale à zéro, de sorte que le finaliseur ne fait rien. Notez que l'outil de finalisation est nécessaire pour être robuste face à un objet qui est en cours de finalisation, dont le constructeur n'a jamais été terminée. Vous êtes requis d'écrire les finaliseurs qui sont de cette forte. C'est encore une autre raison pourquoi vous devriez laisser par écrit les finaliseurs d'experts et de ne pas essayer de le faire vous-même.
Supposons Blah3()
lancers. Le jet qui se passe après que la ressource est allouée. Mais encore une fois, foo
n'est jamais rempli, nous n'avons jamais entrer dans l' finally
, et l'objet est nettoyé par le finaliseur fil. Cette fois, la poignée est non nul, et le finaliseur il nettoie. Encore une fois, l'outil de finalisation est en cours d'exécution sur un objet dont le constructeur n'a jamais réussi, mais le finaliseur fonctionne de toute façon. Évidemment, il doit parce que cette fois, il avait du travail à faire.
Maintenant, supposons Blah2()
lancers. Le lancer se fait après que la ressource est attribué, mais avant d' handle
est rempli! Encore une fois, le finaliseur fonctionne, mais maintenant, handle
est toujours de zéro et nous avons fuite de la poignée!
Vous avez besoin d'écrire extrêmement intelligente code pour empêcher cette fuite se produise. Maintenant, dans le cas de votre Font
des ressources, qui est le diable s'en soucie? Nous avons fuite d'une poignée de police, une grosse affaire. Mais si vous avez absolument, positivement exiger que chaque ressource non managée être nettoyé quel que soit le calendrier des exceptions est alors vous avez un très difficile problème sur vos mains.
Le CLR a résoudre ce problème avec des serrures. Depuis C# 4, les verrous qui utilisent l' lock
déclaration ont été mises en œuvre 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é très soigneusement écrite, de sorte que peu importe ce que les exceptions sont jetés, lockEntered
est définie sur true si et seulement si la serrure a été effectivement prises. Si vous avez des exigences semblables ensuite ce que vous avez à faire est effectivement d'écrire:
public Foo()
{
Blah1();
AllocateResource(ref handle);
Blah2();
Blah3();
}
et écrire AllocateResource
intelligemment comme Monitor.Enter
de sorte que peu importe ce qui se passe à l'intérieur d' AllocateResource
, handle
est rempli si et seulement si il a besoin d'être libéré.
Décrire les techniques pour le faire est au-delà de la portée de cette réponse. Consulter un spécialiste si vous avez cette exigence.