12
votes

Pourquoi la mise en œuvre isisposable a-t-elle conçu la façon dont il est

Jetons un coup d'œil à l'interface imissible infâme: xxx pré>

et une implémentation typique, comme recommandé par MSDN (j'ai omis le chèque si l'objet actuel a déjà été disposé): P >

public class Base : IDisposable
{
    public virtual void Dispose()
    {
        // release managed and unmanaged
        GC.SuppressFinalize(this);
    }

    ~Base()
    {
        // release unmanaged
    }
}

public class Derived : Base
{
    public override void Dispose()
    {
        // release managed and unmanaged
        base.Dispose();
    }

    ~Derived()
    {
        // release unmanaged
    }
}


0 commentaires

8 Réponses :


0
votes

Ma compréhension est que toute la raison de l'Idisposable est de libérer des ressources non gérées, donc je suis confus pourquoi vous indiquez "99% de classes personnalisées n'ont rien sans gré à disposer" - Si vous mettez en œuvre Idisposable, il faut être parce que vous avez des ressources non gérées.

MSDN Idisposable


4 commentaires

Tort. Si vous avez géré des ressources, vous devez toujours mettre en œuvre Idisposable et les éliminer.


Pas toujours vrai. Vous pouvez encapsuler un type avec des ressources natives ou la mise en œuvre d'un type de factorisé.


@Slaks: si les ressources sont complètement gérées, il est moins important (puisqu'un type complètement géré sera géré par le GC correctement ...)


Barret, les ressources "gérées" sont généralement (toujours?) Des objets isolables et pour eux Dispose () doit être enchaîné.



1
votes

MSDN Magazine a un article sur ce modèle .

Cela ne répond pas vraiment à la question, mais vous pouvez utiliser l'extrait de code suivant pour implémenter le motif. P>

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Title>Dispose pattern</Title>
            <Shortcut>dispose</Shortcut>
            <Description>Code snippet for virtual dispose pattern</Description>
            <Author>SLaks</Author>
            <SnippetTypes>
                <SnippetType>Expansion</SnippetType>
                <SnippetType>SurroundsWith</SnippetType>
            </SnippetTypes>
        </Header>
        <Snippet>
            <Declarations>
                <Literal Editable="false">
                    <ID>classname</ID>
                    <ToolTip>Class name</ToolTip>
                    <Default>ClassNamePlaceholder</Default>
                    <Function>ClassName()</Function>
                </Literal>
            </Declarations>
            <Code Language="csharp">
                <![CDATA[
        ///<summary>Releases unmanaged resources and performs other cleanup operations before the $classname$ is reclaimed by garbage collection.</summary>
        ~$classname$() { Dispose(false); }
        ///<summary>Releases all resources used by the $classname$.</summary>
        public void Dispose() { Dispose(true); GC.SuppressFinalize(this); }
        ///<summary>Releases the unmanaged resources used by the $classname$ and optionally releases the managed resources.</summary>
        ///<param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
        protected virtual void Dispose(bool disposing) {
            if (disposing) {
                $end$$selected$
            }
        }]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>


0 commentaires

8
votes

Donc, je me demande, ce qui a conduit à de telles décisions de conception inhabituelles (en faisant une classe dérivée pour remplacer une méthode différente de celle de l'interface et la faire penser à des ressources non gérées qu'elle ne contient probablement pas). Toute pensée sur cette question?

Le problème principal est que Idisposable a été défini après que le cadre était déjà conçu et existant. Il est destiné à gérer une situation que le code géré essaie d'éviter - donc c'est vraiment un cas de bord, s'il est très courant. ;)

Ceci peut être vu, BTW, si vous regardez C ++ / CLI. Il a été conçu après imputable, et, par conséquent, implémente Idisposable de manière beaucoup plus naturelle (destructeurs [ ~ classname ] Devient automatiquement et les finaliseurs [! Nom de classe ] sont traité comme des finaliseurs).

L'autre problème est que Idisposable gère plusieurs situations. J'ai écrit un série de blog totale , parcourir les différentes implémentations qui doivent être utilisées lors de l'emballage du code natif, encapsulant une classe mettant en œuvre Idisposable et l'utiliser avec des types de prises en compte.

Techniquement, vous ne doit mettre en œuvre directement l'interface. La décision de conception permettant de disposer d'un dispositif de vidange virtuel Protected (NOOL) Procédé permet une flexibilité supplémentaire qui ne serait pas facilement et en toute sécurité, manipulée dans une interface publique.


0 commentaires

12
votes

Je sors habituellement les devinettes pour les classes dérivées. Voici mon fichier .Snippet: xxx


5 commentaires

+1 C'est comme ça que je le fais habituellement aussi.


Vous n'avez pas appelé gc.suppressfinise (ceci);


@Sergej vous avez raison; Ce n'est qu'une partie du tout. J'ai mis à jour ma réponse avec le contenu actuel de mon fichier .Snippet, qui est une fusion de différents extraits avec des touches personnelles


Décompresser //base.dispose (); causerait un débordement de pile.


@Luca Non, ça ne le fait pas. Toutefois, si vous avez une classe de base jetable, vous devez corriger la méthode de jettement de la classe de base, doit être virtuelle.



0
votes

Un problème que je vois avec votre implémentation est qu'il existe une poet pour la classe dérivée de ne pas appeler la méthode de la classe de base. Dans ce cas, le GC.SuppressFinalize pourrait ne pas être appelé quand cela devrait, et vous finiriez également d'appeler le finaliseur. J'aime la solution de Will pour s'assurer que GC.SuppressFinalize est appelé. La manière recommandée par MSDN a une sensation similiaire et garantit que GC.SuppressFinalize est appelé si l'objet est disposé par le développeur.


0 commentaires

3
votes

La réponse à cette question et la plupart des autres questions de conception de l'API peuvent être trouvées dans ce livre.

Directives de conception-cadre: Conventions, idiomes et modèles de bibliothèques .NET réutilisables http://www.amazon.com/gp/product/ 0321545613? IE = UTF8 & TAG = BRADABRAMSBLO-20 & LINK_CODE = WQL & Camp = 212361 & Creative = 380601

Ceci est littéralement l'ensemble des règles Les employés de Microsoft utilisent pour créer des API .NET. Les règles sont gratuites (voir ci-dessous), mais le livre a le commentaire qui explique les règles. C'est vraiment un must pour les développeurs .NET.

http://msdn.microsoft.com/en-us/library /b1yfkh5e.aspx


1 commentaires

Également mentionné dans le livre C # efficace



2
votes

Je dirais que c'est mieux : < Pré> xxx


0 commentaires

0
votes

Une caractéristique utile du motif isisposable recommandé est qu'elle permet un motif cohérent pour les types dérivés d'extension des types d'étendue qui implémentent Idisposable, Indépendamment de savoir si le type de base expose une méthode paramètre publique appelée Dispose . Ce n'est vraiment pas trop mauvais un motif, si l'on considère le paramètre comme mannequin utilisé simplement pour donner à la méthode protégée une signature différente d'un dispositif sans paramétrage (); La plus grande faiblesse est que la protection contre le dispositif de démontage redondant n'est pas effectuée de manière à la sécurité du fil.

Une caractéristique non si utile du modèle isisposable recommandé est qu'elle encourage l'utilisation des finaliseurs / destructeurs dans de nombreux cas où ils ne sont pas appropriés. Très rarement devrait être une classe qui dérive de tout autre que System.Object avoir un finisseur pour le nettoyage (les classes peuvent avoir des finaliseurs à des fins d'exploitation d'une défaillance d'une défaillance de disposition). Si une classe détient des références à de nombreux objets gérés et détient également une ressource non gérée, la ressource non gérée doit être déplacée dans sa propre classe wrapper, en la transformant en une ressource gérée. Cette classe wrapper peut soit dériver de quelque chose comme SafeHandle, soit de dériver de l'objet et de définir un finaliseur / destructeur; Soit plan d'action éliminera la nécessité d'un finaliseur / destructeur dans la classe principale.


0 commentaires