2009-07-17 25 views
8

En C# /. NET, ¿hay alguna forma de recibir una notificación antes de que se destruya el objeto señalado por una referencia débil? Básicamente, quiero permitir que un objeto sea recolectado, pero hago algo justo antes de que el objeto sea destruido, sin modificar el código para agregar destructores (ya que no sabré exactamente qué tipos de objetos serán demandados con mi código).C#: ¿Notificación antes de recopilar WeakReference?

Gracias, Robert

Respuesta

5

No puede hacer eso. Sin embargo, lo que puede hacer es ver cuándo se acerca un GC (hay nuevas API de GC en CLR v3.5Sp1 que le permiten hacerlo, GCNotifications)

+0

Supongo que es un lugar tan natural como cualquiera para actualizar los datos serializados. Gracias por la info! –

6

No hay ninguna manera de conseguir esta funcionalidad.

Después de un poco de especulación, no creo que sea posible implementar una función de la manera que usted está describiendo.

Tenga en cuenta que en el punto donde se recoge el objeto que ocupa WeakReference, no hay más referencias (por lo tanto, es de colección). Para que un evento le sirva, debería proporcionar el objeto como parte del evento. Esto significa que la referencia ha pasado de coleccionable a no coleccionable. No hay nada que impida que el código de manejo recupere una referencia en ese objeto. Por lo tanto, el objeto ya no puede considerarse coleccionable. El CLR necesitaría hacer una segunda pasada en el objeto para volver a asegurar que era coleccionable.

Puede ver cómo no se pudo levantar la segunda vez en torno al evento porque podría llevar a objetos incobrables.

Sería un uso indebido de nombres para afirmar que este evento se planteó justo antes de que se recolectó un objeto. Simplemente porque cualquier controlador podría evitar que esto se recopile estableciendo una nueva referencia al objeto. En cambio, debería ser "ObjectMaybeAboutToBeCollected". Esto probablemente no le dará el comportamiento que está buscando.

+0

fresca, gracias ... se puede pensar en alguna forma de conseguir una funcionalidad similar que no sea WeakReferences? –

+0

@Robert, que no sea forma de destructores/desechar No puedo pensar en nada fuera de mi cabeza – JaredPar

+0

Estoy absolutamente de acuerdo, de todos modos, los eventos débiles pueden hacer el truco! Una pequeña trampa, no existen en C#, así que debes implementarlo (es un poco complicado pero básicamente posible). –

0

Tu pregunta no tiene sentido para mí. ¿Dónde se supone que reside el código que se va a llamar? Dado que las referencias débiles se anularán antes de que se destruya el objeto al que se hace referencia, no tiene sentido que sea parte de la clase que hace referencia al objeto destruido. Y ya hay un código en el objeto al que se hace referencia que se llama antes de que se destruya el objeto: ese es el destructor.

¿Cuál es el problema de diseño real que desea resolver? Puede haber una mejor manera.

+0

Quiero crear un sistema donde los objetos se registren para ser serializados al servidor ocasionalmente. Los datos serializados se enviarán al servidor cada pocos minutos (fuera de mi control), por lo que generalmente se calculan los datos serializados para todos los objetos registrados. Sin embargo, si un objeto registrado está siendo destruido, quiero calcular los datos justo antes de que muera (y esos datos se enviarán con el siguiente lote al servidor). –

+2

Eso todavía parece un poco funky. Si va a enviar los datos serializados a un servidor periódicamente, ¿por qué no solo mantener el objeto responsable de la serialización una referencia (no débil) a los objetos registrados y liberarlos una vez que se han cargado? Dada la imprevisibilidad del tiempo de destrucción, de todos modos no querrá depender de ella para la integridad de los datos. –

+0

Necesitan seguir siendo serializados cada cierto tiempo, siempre y cuando estén vivos/cambien. Son las preferencias de la interfaz de usuario de Silverlight (es decir, el ancho de las columnas, etc.), por lo que, mientras los elementos de la interfaz de usuario aún estén presentes, su estado debe guardarse en el servidor. –

0

Por lo que está describiendo, los finalizadores serían una forma mucho mejor enfoque.

0

Sería posible tener una semántica similar a la que describe si una referencia débil con un notificador se considerara de manera similar a un objeto con un finalizador, es decir, cuando el objeto ya no fuera de interés para cualquiera, se pondría en cola para su finalización y notificación; la entrada de la cola se consideraría una referencia en vivo, por lo que el objeto no se recolectaría realmente hasta que no se actuara.

Dado que eso no es posible, el mejor enfoque posible podría ser que todas las referencias de "Me interesen este objeto" apunten a un objeto contenedor ligero que a su vez apunta al objeto real, y tiene el Las referencias "débiles" apuntan a un contenedor diferente que también apunta al objeto real. El primer contenedor debería contener una referencia al segundo, pero no al revés. El primer contenedor debería tener un finalizador que desencadenará el código apropiado cuando salga del alcance.

Lamentablemente, no he visto ninguna implementación completa de dicha estrategia. Hay algunas advertencias importantes a considerar. Entre ellos: (1) los finalizadores nunca deben esperar bloqueos ni hacer nada que pueda arrojar una excepción; (2) el código que accede a otros objetos que podrían haber salido del alcance debe prepararse para la posibilidad de que ya se hayan finalizado, estén en proceso de finalización, estén en espera de finalización o aún tengan referencias en vivo en otro lugar; (3) si un finalizador almacena una referencia rooteada a un objeto finalizable que se ha encontrado elegible para la recolección de elementos no utilizados, dicho objeto puede finalizar aunque la referencia viva exista.

6

.Net 4.0 tiene la solución que necesita: ConditionalWeakTable. Aquí hay un programa corto que demuestra la idea. (Discutido here así)

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"); 
     } 
    } 
} 
Cuestiones relacionadas