2009-07-17 5 views
8

En C# /. NET, est-il possible d'obtenir une notification avant que l'objet pointé par une référence faible soit détruit? Fondamentalement, je veux permettre à un objet d'être collecté, mais faire quelque chose juste avant que l'objet soit détruit, sans modifier le code pour ajouter des destructeurs (puisque je ne saurai pas exactement quels types d'objets seront poursuivis avec mon code).C#: Notification avant que WeakReference ne soit collecté?

Merci, Robert

Répondre

5

Vous ne pouvez pas faire cela. Cependant, ce que vous pouvez faire est de regarder quand un GC approche (il y a de nouvelles API GC dans CLR v3.5Sp1 qui vous permettent de le faire, GCNotifications)

+0

Je suppose que c'est aussi naturel qu'un endroit pour mettre à jour les données sérialisées. Merci pour l'info! –

6

Non, il n'y a aucun moyen pour obtenir cette fonctionnalité. Après un peu de spéculation, je ne crois pas qu'il soit possible d'implémenter une fonctionnalité de la manière que vous décrivez. Considérons qu'au moment où l'objet détenu par un WeakReference est collecté, il n'y a plus de références (d'où il est collectable). Pour qu'un événement vous serve à quelque chose, il doit fournir l'objet dans le cadre de l'événement. Cela signifie que la référence est passée de collectionnable à non collectionnable. Rien n'empêche le code de gestion de reprendre une référence sur cet objet. Par conséquent, l'objet ne peut plus être considéré comme collectable. Le CLR devrait effectuer un second passage sur l'objet pour s'assurer qu'il était récupérable.

Vous pouvez voir comment la deuxième fois autour de l'événement n'a pas pu être déclenchée car cela entraînerait des objets irrécupérables.

Il serait abusif de nommer que cet événement a été déclenché juste avant la collecte d'un objet. Simplement parce que n'importe quel gestionnaire pourrait empêcher que cela soit collecté en établissant une nouvelle référence à l'objet. Au lieu de cela, il devrait être "ObjectMaybeAboutToBeCollected". Cela ne vous donnera probablement pas le comportement que vous recherchez.

+0

Cool, merci ... pouvez-vous penser à des façons d'obtenir des fonctionnalités similaires autres que WeakReferences? –

+0

@Robert, autre que la forme de destructeurs/disposer Je ne peux rien penser du haut de ma tête – JaredPar

+0

Je suis absolument d'accord, de toute façon, les événements faibles pourraient faire l'affaire! Une petite prise, ils n'existent pas en C# donc vous devez l'implémenter (c'est un peu compliqué mais fondamentalement possible). –

0

Votre question n'a aucun sens pour moi. Où est censé résider le code qui va être appelé? Étant donné que les références faibles seront annulées avant que l'objet référencé ne soit détruit, cela n'a aucun sens de faire partie de la classe qui a référencé l'objet détruit sur le point de l'être. Et il y a déjà du code dans l'objet référencé qui est appelé avant que l'objet ne soit détruit - c'est le destructeur.

Quel est le problème de conception que vous voulez résoudre? Il pourrait y avoir un meilleur moyen.

+0

Je veux créer un système où les objets sont enregistrés pour être sérialisés au serveur de temps en temps. Les données sérialisées seront transmises au serveur toutes les quelques minutes (hors de mon contrôle), donc habituellement les données sérialisées pour tous les objets enregistrés sont alors calculées. Cependant, si un objet enregistré est détruit, je veux calculer les données juste avant qu'elles ne meurent (et ces données seront envoyées avec le prochain lot au serveur). –

+2

Cela semble encore un peu génial. Si vous allez envoyer périodiquement les données sérialisées à un serveur, pourquoi ne pas simplement laisser l'objet responsable de la sérialisation conserver une référence (non défaillante) aux objets enregistrés et les libérer une fois qu'ils ont été téléchargés? Étant donné l'imprévisibilité du moment de la destruction, vous ne voudriez pas en dépendre pour l'intégrité des données, de toute façon. –

+0

Ils doivent continuer à être sérialisés de temps en temps tant qu'ils sont vivants/changeants. Il s'agit des préférences de l'interface utilisateur Silverlight (c'est-à-dire des largeurs de colonnes, etc.), de sorte que tant que les éléments de l'interface utilisateur sont toujours présents, leur état doit être enregistré sur le serveur. –

0

Pour ce que vous décrivez, les finaliseurs seraient un meilleur moyen approche.

0

Il serait possible d'avoir une sémantique similaire à celle que vous décrivez si une référence faible avec un notifiant était considérée comme un objet avec un finaliseur, ce qui veut dire que lorsque l'objet ne présentait plus d'intérêt pour n'importe qui, il ferait la queue pour la finalisation et la notification; l'entrée de la file d'attente serait considérée comme une référence en direct, de sorte que l'objet ne serait pas réellement collecté avant d'avoir été traité. Puisque cela n'est pas possible, la meilleure approche possible serait probablement de faire en sorte que tous les renvois «Je m'intéresse à cet objet» pointent vers un objet wrapper léger qui pointerait vers l'objet réel, et aurait Les références "faibles" pointent vers un wrapper différent qui pointerait également vers l'objet réel. Le premier wrapper doit contenir une référence à la seconde, mais pas l'inverse. Le premier wrapper devrait avoir un finaliseur qui déclenchera le code approprié quand il sera hors de portée.

Malheureusement, je n'ai vu aucune implémentation complète d'une telle stratégie. Il y a quelques mises en garde importantes à considérer. Parmi eux: (1) les finaliseurs ne devraient jamais attendre sur les verrous, ni faire quoi que ce soit qui pourrait jeter une exception; (2) le code qui accède à d'autres objets qui auraient pu être hors de portée doit être préparé à la possibilité qu'ils aient déjà été finalisés, soient en cours de finalisation, soient en attente de finalisation, ou aient encore des références en direct ailleurs; (3) si un finaliseur stocke une référence enracinée à un objet finalisable qui a été trouvé éligible pour la récupération de place, un tel objet peut être finalisé même si la référence live existe.

6

.Net 4.0 a la solution dont vous avez besoin: ConditionalWeakTable. Voici un petit programme qui démontre l'idée. (Voir here ainsi)

using System; 
using System.Runtime.CompilerServices; 

namespace GCCollectNotification 
{ 
    class ObjectToWatch { } 

    class Notifier 
    { 
     public object ObjectToWatch { get; set; } 
     ~Notifier() { Console.WriteLine("object is collected"); } 
    } 

    class Program 
    { 
     private ConditionalWeakTable<object, Notifier> map 
      = new ConditionalWeakTable<object, Notifier>(); 

     public void Test() 
     { 
      var obj = new ObjectToWatch(); 
      var notifier = map.GetOrCreateValue(obj); 
      notifier.ObjectToWatch = obj; 
     } 

     static void Main(string[] args) 
     { 
      new Program().Test(); 

      GC.Collect(); 
      GC.WaitForPendingFinalizers(); 

      // "object is collected" should have been printed by now 

      Console.WriteLine("end of program"); 
     } 
    } 
} 
Questions connexes