2010-01-06 11 views
1

J'ai posé une question plus tôt aujourd'hui, mais je pense que je dois l'aborder d'une manière différente (en plus de cela il y avait un "raccrocher" en ce qui concerne DataSet) .C# - Question de disposition liée à la conception (prendre deux)

est ici une classe qui encapsule la création d'une police (autrement dit, il est en train de lire les données à partir d'un fichier xml et crée une police, lors de l'exécution, en fonction de ce qu'il lit dans ce fichier):

public class FontCreator 
{ 
    private Font m_TheFont = null; 

    public FontCreator(... some parameters ...) 
    { 
     m_TheFont = GetTheFont(); 
    } 

    public Font TheFont 
    { 
     return m_TheFont; 
    } 

    private Font GetTheFont() 
    { 
     // code, and more code, that eventually leads to: 

     Font f = new Font(fntFamily, fntSize, fntStyle); 
     return f; 
    } 
} 

le consommateur de la classe FontCreator ressemble à quelque chose comme:

public class TheConsumer() 
{ 
    private FontCreator m_FontCreator = null; 

    public TheConsumer() 
    { 
     m_FontCreator = new m_FontCreator(... some parameters ...); 
     Initialize(); 
    } 

    private void Initialize() 
    { 
     InitializeThis(); 
     InitializeThat(); 
    } 

    private void InitializeThis() 
    { 
     .... some code ... 
     SomeObject.ApplyFont(m_FontCreator.TheFont); 
    } 

    private void InitializeThat() 
    { 
     ... some code ... 
     SomeObject.ApplyFont(m_FontCreator.TheFont); 
    } 
} 

Quel code ajoutez-vous, et où, pour faire en sorte que « TheFont » de méthode Dispose est explicitement appelé?

+0

d'abord vous demander pourquoi vous avez besoin de disposer d'une police, quel est le but de votre disposition? Y at-il des ressources importantes que vous essayez de libérer? .. il semble que vos questions tentent de se débarrasser des choses juste pour les mettre au rebut. –

+0

@Stan R: Attendez, quoi !? À partir de ce lien msdn: http://msdn.microsoft.com/en-us/library/yh598w02.aspx – JustLooking

+0

@Stan R (suite): a) «Fichier et police sont des exemples de types gérés qui accèdent aux ressources non managées» . b) "En règle générale, lorsque vous utilisez un objet IDisposable, vous devez le déclarer et l'instancier dans une instruction using L'instruction using appelle correctement la méthode Dispose sur l'objet, et (lorsque vous l'utilisez comme indiqué précédemment) il provoque également l'objet lui-même à sortir de la portée dès que Dispose est appelé " – JustLooking

Répondre

3

Si vous ne souhaitez pas conserver une référence à TheFont après avoir été d'abord utilisé, puis appeler sa méthode Dispose dans votre constructeur, juste après Initialize. Si vous souhaitez conserver TheConsumer en vie pendant un certain temps et maintenir une référence à TheFont, cela devient plus intéressant. Deux options:

  1. Vous pouvez faire appel à la méthode d'élimination de TheFont à partir du Destructeur de l'objet TheConsumer. Ce n'est pas la pratique courante et a des problèmes. Principalement, ce n'est pas appelé jusqu'à ce que la collecte des ordures se passe. Mieux vaut:
  2. Vous pouvez rendre l'objet TheConsumer implémenter IDisposable et appeler TheFont.Dispose depuis TheConsumer.Dispose. Puisque TheConsumer implémente IDisposable, le code qui utilise it devrait appeler sa méthode Dispose.

Modifier en réponse à un commentaire dur! Oui, j'aurais dû préciser de n'utiliser que 1 en plus à 2, voire pas du tout. Je sais que tous les développeurs sont supposés à remarquer quand IDisposable est implémenté, mais ils ne le font souvent pas. Si la ressource gérée référencée peut vraiment rester longtemps et causer des problèmes si elle n'est pas correctement éliminée, j'ai parfois un appel de méthode de sécurité Dispose() dans le destructeur de l'objet contenant la référence. Est-ce que c'est si faux? :)

+0

Henk pourriez-vous élaborer? – JustLooking

+0

@Henk: Encore un suivi: Disons que j'implémente IDisposable sur FontCreator, mais je n'ai pas de Finalizer. Dans ma fonction Dispose, j'appelle: m_TheFont.Dispose(). Maintenant, quelqu'un arrive, ils utilisent FontCreator, mais n'utilisez pas "using" ou appelez Dispose sur le getter "TheFont". En outre, ils n'appellent pas Dispose sur FontCreator. Par conséquent, m_TheFont.Dispose() n'est jamais appelé. N'avons-nous pas simplement perdu la mémoire non managée que m_TheFont utilisait? Même si nous sommes un niveau supprimé? – JustLooking

+1

@Henk: Dispose() n'est pas appelé par GC, non? Maintenant, faire en sorte que le GC traite avec une méthode de finalisation a un coût (et peut faire en sorte que l'objet reste plus longtemps), mais cela ne vaut-il pas la peine de s'assurer qu'une méthode Dispose importante est appelée? Pour moi, cela ressemble à un jugement. Supposons qu'un objet BigShot ait une longue durée de vie et qu'il se réfère à de nombreux objets LittleShot (implémentant IDisposable) dont les ressources non managées ont besoin d'être nettoyées. Ne devrions-nous pas peser la création d'un finaliseur BigShot en nous assurant que toutes ces ressources sont nettoyées? Cela me semble être un appel de jugement. –

3
public TheConsumer() 
{ 
    using (m_FontCreator = new m_FontCreator(... some parameters ...)) 
    { 
     Initialize(); 
    } 
} 
+0

Donc ce que vous dites est que je devrais modifier la classe "FontCreator" pour être IDisposable? Parce que votre code ne compilera que si je le fais. Si c'est le cas (faites FontCreator IDisposable), puis-je voir comment vous feriez cela? THX. – JustLooking

+1

FontCreator ne pas mettre en œuvre IDisposable ... –

+2

vous avez fait une typologique il devrait être nouveau FontCreater :) –

3

Je suis confus, si vous voulez utiliser rapidement l'objet creater de la police puis mettre en œuvre IDisposable sur le FontCreater et utiliser

using(m_FontCreator = new FontCreater(....)) 
{ 
    InitializeThis(); 
    InitializeThat(); 
} 

Si vous devez garder l'instance de la FontCreater par la durée de vie TheConsumer , puis implémentez IDisposablesur les deux classesFontCreater et TheConsumer.

public class TheConsumer : IDisposable 
{ 
    void Dispose() 
    { 
    if(m_FontCreator != null) 
      m_FontCreator.Dispose(); 
    } 
} 

puis utilisez TheConsumer classe comme si

using(TheConsumer consumer = new TheConsumer(....)) 
{ 
    .... 
} 
1

Réponse 1: éviter. Ne gardez pas les objets contenant des ressources non gérées plus longtemps que nécessaire.

Réponse 2: Si vous avez besoin des champs incorporés comme indiqué dans votre code, les classes FontCreator et Consumer doivent implémenter IDisposable. Mais pas un destructeur (Finalizer).
L'argument principal pour cela est que FontCreator est le «propriétaire» de la police et devrait donc prendre la responsabilité. Et le Consommateur est responsable du Créateur de la même manière.

Comme d'autres l'ont noté, il semble que vous pouvez au moins éviter le champ m_FontCreator dans la classe Consumer. Mais cela dépend du reste du code, m_FontCreator est-il utilisé ailleurs?

+0

Oh, je suis d'accord. 1) Mais, dans la situation ci-dessus, comment puis-je l'éviter? 2) Plus important encore, pourquoi pas de Finalizer? Msdn: "Notez que même si vous fournissez un contrôle explicite via Dispose, vous devez effectuer un nettoyage implicite à l'aide de la méthode Finalize Finalize fournit une sauvegarde pour empêcher les fuites permanentes de ressources si le programmeur ne parvient pas à appeler Dispose" Ceci est le lien : http://msdn.microsoft.com/en-us/library/b1yfkh5e(VS.71).aspx – JustLooking

+1

Re le finaliseur: si vous passez par le code, vous verrez que le paramètre de disposition assure que le finaliseur ne fait rien quand il ne sont pas des ressources non managées. Mieux vaut le laisser de côté, il y a un coût considérable. Le texte s'applique aux objets _with_ ressources non gérées, comme Font. –