Appel de base.Dispose() automatiquement à partir de classes dérivées
Edition - Nouveau Question
Ok permet de reformuler la question de manière plus générale.
L'aide de la réflexion, est-il possible d'appeler de manière dynamique lors de l'exécution d'une méthode de classe de base que vous pouvez être prépondérantes. Vous ne pouvez pas utiliser la "base" mot-clé au moment de la compilation, car vous ne pouvez pas être sûr qu'il existe. Au moment de l'exécution, je veux la liste de mes ancêtres méthodes et d'appeler l'ancêtre des méthodes.
J'ai essayé d'utiliser GetMethods() et que telle, mais tout ce qu'ils sont de retour "pointeurs" pour la plupart des dérivés de la mise en œuvre de la méthode. Pas une mise en œuvre sur une base de classe.
Fond
Nous sommes en train de développer un système en C# 3.0 avec un relativement importante de la hiérarchie de classe. Certaines de ces classes, n'importe où dans la hiérarchie, ont des ressources qui doivent être
éliminés, ceux de mettre en œuvre la IDisposable interface.
Le Problème
Maintenant, pour faciliter la maintenance et refactoring du code, je voudrais trouver un moyen, pour les classes de mise en œuvre de IDisposable,
pour "automatiquement" appel de base.Dispose(bDisposing) si tout ancêtres met également en œuvre IDisposable. De cette façon, si un peu de classe à un niveau supérieur dans la hiérarchie commence à mettre en œuvre
ou s'arrête la mise en œuvre de IDisposable qui seront pris en charge automatiquement.
La question est de deux plis.
- Tout d'abord, trouver si tout ancêtres implémente IDisposable.
- Deuxième, appel de base.Dispose(bDisposing) sous certaines conditions.
La première partie, la recherche sur les ancêtres de mise en œuvre de IDisposable, j'ai été en mesure de traiter avec.
La deuxième partie est difficile. Malgré tous mes
efforts, je n'ai pas été en mesure d'appeler la base.Dispose(bDisposing) à partir d'une classe dérivée. Toutes mes tentatives ont échoué. Ils causée
des erreurs de compilation ou de l'appelé la mauvaise méthode dispose (), qui est la plus dérivée, donc en boucle pour toujours.
Le principal problème est que vous ne peut pas se réfèrent en fait à la base.Dispose() directement dans votre code si il n'y a pas une telle chose comme un
ancêtre de la mettre en œuvre (être rappelé qu'il n'ont pas d'ancêtres encore la mise en œuvre de IDisposable, mais je veux que le dérivé de code pour être prêt quand et si ces
une chose qui se passe dans le futur). Qui nous laissent avec le Réflexion mécanismes, mais je n'ai pas trouvé une bonne façon de le faire. Notre code est assez rempli avec
avancée des techniques de réflexion et je pense que je n'ai pas manqué de rien d'évident là.
Ma Solution
De mon mieux était encore d'avoir quelques conditionnel du code à l'aide de code commenté. La modification de la IDisposable hiérarchie soit de briser la construction
(si pas de IDisposable ancêtre existe) ou lève une exception (si il y a IDisposable ancêtres, mais de la base.Jeter n'est pas appelé).
Voici un code que j'ai écris pour vous montrer ce que mon Aliéner(bDisposing) méthode ressemble. Je suis en train de mettre ce code à la fin de tous les Dispose()
méthodes tout au long de la hiérarchie. Toutes les nouvelles classes sont créées à partir de modèles, qui comprend également présent code.
public class MyOtherClassBase
{
//...
}
public class MyDerivedClass : MyOtherClassBase, ICalibrable
{
private bool m_bDisposed = false;
~MyDerivedClass()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool bDisposing)
{
if (!m_bDisposed) {
if (bDisposing) {
//Dispose managed resources
}
//Dispose unmanaged resources
}
m_bDisposed = true;
Type baseType = typeof(MyDerivedClass).BaseType;
if (baseType != null) {
if (baseType.GetInterface("IDisposable") != null) {
//If you have no ancestors implementing base.Dispose(...), comment
//the following line AND uncomment the throw.
//
//This way, if any of your ancestors decide one day to implement
//IDisposable you will know about it right away and proceed to
//uncomment the base.Dispose(...) in addition to commenting the throw.
//base.Dispose(bDisposing);
throw new ApplicationException("Ancestor base.Dispose(...) not called - "
+ baseType.ToString());
}
}
}
}
Donc, je me demande est-il un moyen de l'appel de base.Dispose() automatiquement/conditionnellement à la place?
Plus De Fond
Il existe un autre mécanisme dans l'application où tous les objets sont enregistrés avec une classe principale. La classe vérifie si ils mettent en œuvre IDisposable.
Si oui, ils sont éliminés de façon appropriée par l'application. Cela évite d'avoir le code à l'aide des classes de traiter avec
appelant Dispose() tout autour par eux-mêmes. Ainsi, l'ajout de IDisposable à une classe qui n'a pas d'ancêtre de l'histoire de IDisposable fonctionne encore parfaitement.
OriginalL'auteur Philibert Perusse | 2008-09-16
Vous devez vous connecter pour publier un commentaire.
Le modèle standard est pour votre classe de base pour mettre en œuvre IDisposable et la non-virtuel méthode dispose (), et de mettre en œuvre un virtuel méthode dispose(bool), que les classes qui détiennent des ressources disponibles doit l'emporter. Ils doivent toujours consulter leur base méthode dispose(bool), qui va de la chaîne jusqu'à la classe supérieure dans la hiérarchie par la suite. Seules les classes qui la dépassent, elle sera appelée, de sorte que la chaîne est en général assez court.
Finaliseurs, orthographié ~Classe en C#: Ne pas. Très peu de cours en aura besoin, et il est très facile de accidentellement de garder les gros graphes d'objets autour, parce que les finaliseurs besoin d'au moins deux collections avant que la mémoire est libérée. Sur la première collection, après que l'objet n'est plus référencé, il est mis dans une file d'attente des finaliseurs à exécuter. Ces actions sont exécutées sur une autre, thread dédié qui ne fonctionne finaliseurs (si elle est bloquée, pas plus finaliseurs et votre utilisation de la mémoire explose). Une fois le finaliseur a été exécuté, la prochaine collection qui recueille les génération permettra de libérer de l'objet et rien d'autre, c'est le référencement qui n'est pas autrement référencés. Malheureusement, parce qu'il survit à la première collection, elle sera placée dans la génération plus âgée qui est perçue moins fréquemment. Pour cette raison, vous devez Disposer tôt et souvent.
En général, vous devez mettre en œuvre une petite classe wrapper qui seulement gère la ressource de vie et de mettre en œuvre un outil de finalisation sur cette classe, plus IDisposable. L'utilisateur de la classe doit alors appeler dispose sur ce quand il est éliminé. Il ne devrait pas être un back-link à l'utilisateur. De cette façon, que la chose qui a réellement besoin de finalisation se termine sur la file d'attente de finalisation.
Si vous allez avoir besoin de n'importe où dans la hiérarchie de la classe de base, qui implémente IDisposable devrait mettre en œuvre l'outil de finalisation et d'appel dispose(bool), en passant false au paramètre.
AVERTISSEMENT pour les développeurs Windows Mobile (VS2005 et 2008 .NET Compact Framework 2.0 et 3.5): beaucoup de non-contrôle que vous déposez sur votre concepteur de surface, par exemple les barres de menu, les minuteries, les HardwareButtons, dériver à partir du Système.ComponentModel.Composant, qui met en œuvre un outil de finalisation. Pour les projets de bureau, Visual Studio ajoute les composants d'un Système.ComponentModel.Conteneur nommé
components
, qui génère du code pour en Disposer lorsque le formulaire est Disposé - il à son tour, Dispose de tous les composants qui ont été ajoutés. Pour les projets mobiles, le code de Disposercomponents
est généré, mais la suppression d'une composante sur la surface ne génère pas le code pour l'ajouter àcomponents
. Vous devez le faire vous-même dans votre constructeur après l'appel InitializeComponent.Il n'y a pas une telle chose comme un défaut finalizer. Si vous ne créez pas un, il n'existe pas et n'finalisation aura lieu.
Les Classes qui implémentent IDisposable généralement doit mise en œuvre d'un outil de finalisation en collaboration avec les jetables standard modèle, qui comprend un appel à la GC.SuppressFinalize(ce) dans "public Dispose()" afin d'éviter le coût d'un finaliseur lorsque l'appelant dispose correctement.
Si un objet implémente un finaliseur, ni elle, , ni n'importe quel objet pour lequel elle détient une référence directe ou indirecte,, qui peut être récupérée par le garbage collector jusqu'à ce que après le finaliseur a exécuter. Par conséquent, les objets avec les finaliseurs devrait éviter de tenir des références à rien ne sont pas nécessaires pour la finalisation. Par conséquent, si une classe contient des références à des choses d'une classe dérivée n'aurez pas besoin de finalisation, puis la classe dérivée ne devrait pas mettre en œuvre un finaliseur lui-même. Au lieu de cela, il doit encapsuler les informations nécessaires pour la finalisation de son propre objet finalisable.
Beaucoup de
Dispose()
méthodes n'existent que pour appeler les variables de membre’Dispose()
méthodes. Tels sont les classes pas mise en œuvre d'un outil de finalisation; seules les classes qui directement propres ressources non managées devrait jamais mettre en œuvre des outils de finalisation. Je pense que la meilleureDispose()
modèle est trouvé dansWindows.Forms
:protected virtual Dispose(bool disposing) { if (disposing) {member.Dispose();} idempotentUnmanagedResourceFree(); } public void Dispose() { Dispose(true); }
. AppelDispose(false)
seulement à partir de l'outil de finalisation et seulement écrire un tel finaliseur si cette classe détient directement unmanged ressources.OriginalL'auteur Mike Dimmick
Personnellement, je pense que vous pourriez être mieux de la manipulation de ce avec quelque chose comme FxCop. Vous devriez être en mesure d'écrire une règle de vérification afin de voir si lors de la création d'un objet qui implémente IDisposable que vous utilisez à l'aide d'instruction.
Il semble un peu sale (pour moi) pour automatiquement disposer d'un objet.
OriginalL'auteur Bryant
Il n'est pas "accepté" manière de faire. Vous voulez vraiment faire de votre nettoyer logique (qu'il s'exécute à l'intérieur d'une élimination ou d'un outil de finalisation) aussi simple que possible afin de ne pas échouer. L'utilisation de la réflexion à l'intérieur de la jeter (et surtout un outil de finalisation) est généralement une mauvaise idée.
Autant que la mise en œuvre des outils de finalisation, en général, vous n'avez pas besoin d'. Les finaliseurs ajouter un coût à votre objet et sont difficiles à écrire correctement, comme la plupart des hypothèses que vous pouvez normalement faire à propos de l'état de l'objet et le moteur d'exécution ne sont pas valides.
Voir ce l'article pour plus d'informations sur le modèle dispose.
OriginalL'auteur Scott Dorman
OriginalL'auteur SUmeet Khandelwal
Si vous souhaitez utiliser [basetype].Invoke("Jeter"...) alors on pourrait mettre en œuvre l'appel de fonction sans le débogueur de se plaindre. Ensuite, quand le type de base en fait implémente l'interface IDisposable il va lancer l'appel correct.
OriginalL'auteur
Si vous souhaitez utiliser [basetype].Invoke("Jeter"...) alors on pourrait mettre en œuvre l'appel de fonction sans le débogueur de se plaindre. Ensuite, quand le type de base en fait implémente l'interface IDisposable il va lancer l'appel correct.
OriginalL'auteur Adam Driscoll
De l'essayer. C'est une ligne de plus à la méthode dispose (), et les appels de l'ancêtre de disposer, si elle existe. (Notez que
Dispose(bool)
n'est pas un membre deIDisposable
)OriginalL'auteur Steve Cooper
De cette façon, vous êtes responsable de mettre en œuvre droite, le premier de la classe qui est IDisposable.
OriginalL'auteur Sunny Milenov