11
votes

C # Méthode d'élimination abstraite

J'ai une classe abstraite qui implémente Idisposable, comme:

public abstract class ConnectionAccessor : IDisposable
{
    public abstract void Dispose();
}


2 commentaires

Pourquoi avez-vous besoin d'ajouter un dispositif () à votre interface? S'il est hérité de Idisposable, la méthode Dispose () fait déjà partie de votre interface.


Seth, vous devez mettre en œuvre tous les membres de l'interface. Le quitter ne compilerait pas.


5 Réponses :


10
votes

L'avertissement vous indique essentiellement de mettre en œuvre le Dispose modèle dans votre classe.

Le code résultant devrait ressembler à ceci: xxx


1 commentaires

+1 Pour mentionner le motif, et soulignant que le dispositif d'élimination (BOOL) doit être la méthode virtuelle / abstrait que les classes dérivées doivent être appliquées ou remplacées, ne pas disposer ().



15
votes

Vous devez suivre le modèle classique pour la mise en œuvre Disposer . Rendre Dispose () Virtual est considéré comme une mauvaise pratique, car le motif conventionnel met l'accent sur la réutilisation du code dans "Nettoyage géré" (Calling client API Dispose () directement ou via en utilisant ) et "nettoyage non géré" (finisseur d'appel GC). Pour rappeler, le motif est-ce: xxx

clé ici est qu'il n'y a pas de duplication entre le finaliseur et le Dispose pour un nettoyage non géré, et pourtant toute classe dérivée peut prolonger les deux et nettoyage non géré.

Pour votre cas, ce que vous devriez faire est celui-ci: xxx

et laisser tout le reste tel quel. Même cela est de valeur douteuse, car vous appliquez vos classes dérivées pour mettre en œuvre Disposer maintenant - et comment savez-vous que tous en ont besoin? Si votre classe de base n'a rien à disposer, mais la plupart des classes dérivées sont probablement (à quelques exceptions près, peut-être), il suffit de fournir une implémentation vide. C'est ce que system.io.io.stream (abstrait) est donc précédent.


3 commentaires

Je préférerais beaucoup que les gens soient invités à faire cela: nitoprograms.blogspot.com/2009/08/... - Le modèle serait un enfer de beaucoup plus simple s'il n'avait pas de soutien intégré aux cours qui mixent les ressources gérées et non gérées.


Si une classe dérivée d'une classe particulière devra mettre en œuvre Idisposable, et si un objet qui s'attend à ce que le type de classe de base puisse finir de manière à contenir la dernière référence utile, la classe de base doit mettre en œuvre Idisposable, même si le vaste La majorité des classes dérivées n'en auront pas besoin.


Ce modèle n'est-il pas défendu depuis .net 2? Ma compréhension est que les ressources non gérées doivent être détenues par une certaine forme de la manipulation de SafeHandle / Critique et des classes normales d'utilisation des utilisateurs normaux n'ont plus besoin d'un finaliseur. Et leur Dispose Just Appels Dispose sur la poignée.



-1
votes

L'avertissement est intéressant cependant. Eric LIPPERT, l'un des concepteurs C #, a blogué sur pourquoi les messages d'erreur devraient être "diagnostic mais pas prescriptif: décrivez le problème, pas la solution". lire ici .


3 commentaires

Ce n'est pas un message d'erreur de compilateur. C'est le résultat de la gestion d'un outil qui indique spécifiquement des choses problématiques. Il n'y a rien de mal à souligner aussi la solution.


Sûr. Je voulais juste partager le lien. Il est venu à moi parce que le message n'indique pas le problème mais donne uniquement la solution. J'espère qu'il y avait un message précédent faisant cela.


C'est une sorte d'tangentielle à la question initiale.



2
votes

Bien que cela semble un peu comme la cueillette de nit, le conseil est valide. Vous indiquez déjà que vous vous attendez à ce que les sous-types de Connectioncessor auront quelque chose em> qu'ils doivent disposer. Par conséquent, il semble préférable de s'assurer que le nettoyage approprié est effectué (en termes d'appel GC.SuppressFinalize) par la classe de base plutôt que de s'appuyer sur chaque sous-type pour le faire.

J'utilise le modèle d'élimination mentionné dans Bruce Wagers Réservez efficace C # qui est fondamentalement: P>

public class BaseClass : IDisposable
{
    private bool _disposed = false;
    ~BaseClass()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(true);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
            return;

        if (disposing)
        {
            //release managed resources
        }

        //release unmanaged resources

        _disposed = true;
    }
}

public void Derived : BaseClass
{
    private bool _disposed = false;

    protected override void Dispose(bool disposing)
    {
        if (_disposed) 
            return;

        if (disposing)
        {
            //release managed resources
        }

        //release unmanaged resources

        base.Dispose(disposing);
        _disposed = true;
    }


0 commentaires

4
votes

La seule grille que j'aurais avec les réponses fournies jusqu'à présent, c'est qu'ils supposent tous que vous BESOIN d'avoir un finisseur, ce qui n'est pas nécessairement le cas. Il existe une surcharge de performance assez significative associée à la finalisation, que je ne voudrais pas imposer à toutes mes classes dérivées si ce n'était pas nécessaire.

voir Ce blog post Par Joe Duffy, qui explique lorsque vous pouvez ou non avoir besoin d'un finaliseur et comment implémenter correctement le modèle d'élimination dans les deux cas.
Résumant le blog de Joe's Blog, à moins que vous fassiez quelque chose de relativement bas sur la mémoire non gérée, vous ne devriez pas implémenter un finisseur. En règle générale, si votre classe ne détient que des références à des types gérés qui implémentent Idisposables elles-mêmes, vous n'avez pas besoin du finaliseur (mais devriez mettre en œuvre les ressources isisposables et disposer de ces ressources). Si vous allociez des ressources non gérées directement à partir de votre code (Pinvoke?) Et ces ressources doivent être libérées, vous en avez besoin. Une classe dérivée peut toujours ajouter un finaliseur s'il en ait vraiment besoin, mais obliger toutes les classes dérivées à avoir un finaliseur en la mettant dans la classe de base entraîne l'impact de toutes les classes dérivées par le succès des performances des objets finalisables lorsque ces frais généraux peuvent ne pas être nécessaire.