2013-03-05 20 views
5

için başvuruda bulunur Bellek sızıntılarını önlemek için WeakEventManager kullanıyorum ve bunları aşırı kullanmaya başladım. Bu işe yaramazsa o, Şimdi ben çabuk farkWeakEventManager, Abone


public static void AddWeakPropertyChanged(this INotifyPropertyChanged item, Action handler) 
{ 
    PropertyChangedEventManager.AddHandler(item, (s, e) => handler(e.PropertyName), string.Empty); 
} 

: ben gibi INotifyPropertyChanged örneğin uzatma yöntemleri yarattı. Aslında, zayıf olay işleme için gerçekten anonim yöntemler kullanamazsınız. (Doğru bir şekilde anladığım takdirde, derleyici, işleyiciye sahip olan bir 'kapatma sınıfı' oluşturur (başvurulan değerleri tutturmak için), ancak kapatma sınıfınız herhangi bir yerde referans alınmadığından, GC bunu temizler ve olay işleyicisi # 1: Bu doğru mu? Demek istediğim doğrudur, daha sonra zayıf bir olay işleyicisi için anonim bir yöntem (veya lambda) kullanıldığında, işleyici yalnızca GC bu sırada çalışmadığı zaman çağrılır (örn. Bu belirsizdir)?

Eh, öyle yaptım, bu yüzden doğru yaptığından emin olmak için bazı birim testleri yaptım. i aşağıdaki birim testi çarpana kadar bu yolunda görünüyordu:


     class DidRun 
     { 
      public bool Value { get; set; } 
     } 
     class TestEventPublisher 
     { 
      public event EventHandler<EventArgs> MyEvent; 
      public void RaiseMyEvent() 
      { 
       if (MyEvent != null) 
        MyEvent(this, EventArgs.Empty); 

      } 
     } 
     class TestClosure 
     { 
      public DidRun didRun { get; set; } 
      public EventHandler<EventArgs> Handler { get; private set; } 
      public TestClosure() 
      { 
       this.Handler = new EventHandler<EventArgs>((s, e) => didRun.Value = true); 
      } 
     } 
     [TestMethod] 
     public void TestWeakReference() 
     { 
      var raiser = new TestEventPublisher(); 
      var didrun = new DidRun(); 
      var closure = new TestClosure { didRun = didrun }; 
      WeakEventManager<TestEventPublisher, EventArgs>.AddHandler(raiser, "MyEvent", closure.Handler); 
      closure = null; 

      GC.Collect(); 
      GC.Collect(); 
      raiser.RaiseMyEvent(); 
      Assert.AreEqual(false, didrun.Value); 
     } 

Soru 2: herkes neden bu test başarısız oluyor benim açıklayabilir misiniz?

Beklenti: Burada herhangi bir kapanış yok (onları dışarı çıkardım, ne olduğundan emin olmak için), WeakEventManager ile bir etkinliğe abone olan bir nesne (kapama) var ve sonra ona referans (closure = null;).

Eski kapatma sınıfımı temizlemek için 2 GC.Collect() çağrısını bekliyordum, bu nedenle WeakEventManager aboneyi düşürür ve işleyiciyi çalıştırmaz, ancak sınama başarısız olur. Herhangi bir fikir?

DÜZENLEME: şimdi onlar

cevap

3

olan Maalesef jenerik argümanlar, görünür değildi Sen ona bir atıf yoktur eğer GC sizin lambda etrafında oluşturulan kapatma toplayacak doğru.

Ünitenizde sınama yaptığınızda, TestClosure yerel örneğini sıfırlarsınız, ancak işleyicinin WeakEventManager öğesine bir başvuru kaynağını geçirdiyseniz, bir örnek TestClosure.

orijinal konuya gelince
class DidRun 
{ 
    public bool Value { get; set; } 
} 

class TestEventPublisher 
{ 
    public event EventHandler<EventArgs> MyEvent; 
    public void RaiseMyEvent() 
    { 
     if (MyEvent != null) 
      MyEvent(this, EventArgs.Empty); 
    } 
} 

class TestClosure 
{ 
    static public EventHandler<EventArgs> Register(TestEventPublisher raiser, DidRun didrun) 
    { 
     EventHandler<EventArgs> handler = (s, e) => didrun.Value = true; 
     WeakEventManager<TestEventPublisher, EventArgs>.AddHandler(raiser, "MyEvent", handler); 
     return handler; 
    } 
} 

[TestMethod] 
public void Test1() 
{ 
    var raiser = new TestEventPublisher(); 
    var didrun = new DidRun(); 

    TestClosure.Register(raiser, didrun); 

    // The reference to the closure 'handler' is not being held, 
    // it may or may not be GC'd (indeterminate result) 

    raiser.RaiseMyEvent(); 
    Assert.IsTrue(didrun.Value); 
} 

[TestMethod] 
public void Test2() 
{ 
    var raiser = new TestEventPublisher(); 
    var didrun = new DidRun(); 

    // The reference to the closure 'handler' is not being held, it's GC'd 
    TestClosure.Register(raiser, didrun); 

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

    raiser.RaiseMyEvent(); 
    Assert.IsFalse(didrun.Value); 
} 

[TestMethod] 
public void Test3() 
{ 
    var raiser = new TestEventPublisher(); 
    var didrun = new DidRun(); 

    // Keep local copy of handler to prevent it from being GC'd 
    var handler = TestClosure.Register(raiser, didrun); 

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

    raiser.RaiseMyEvent(); 
    Assert.IsTrue(didrun.Value); 
} 

, sen önlemek için işleyici (kapatma) kaydetmek için deneyin: Yani işleyici

Ben bu örneklerin kapatılması ile sorun göstermek inanıyoruz ... yaşıyor GC'd olmak. Bu tarafından şaşkın,

// ConditionalWeakTable will hold the 'value' as long as the 'key' is not marked for GC 
static private ConditionalWeakTable<INotifyPropertyChanged, EventHandler<PropertyChangedEventArgs>> _eventMapping = 
    new ConditionalWeakTable<INotifyPropertyChanged, EventHandler<PropertyChangedEventArgs>>(); 

public static void AddWeakPropertyChanged(this INotifyPropertyChanged item, Action<string> handlerAction) 
{ 
    EventHandler<PropertyChangedEventArgs> handler; 

    // Remove any existing handler for this item in case it's registered more than once 
    if (_eventMapping.TryGetValue(item, out handler)) 
    { 
     _eventMapping.Remove(item); 
     PropertyChangedEventManager.RemoveHandler(item, handler, string.Empty); 
    } 

    handler = (s, e) => handlerAction(e.PropertyName); 

    // Save handler (closure) to prevent GC 
    _eventMapping.Add(item, handler); 

    PropertyChangedEventManager.AddHandler(item, handler, string.Empty); 
} 

class DidRun 
{ 
    static public string Value { get; private set; } 
    public void SetValue(string value) { Value = value; } 
} 

[TestMethod] 
public void Test4() 
{ 
    var property = new ObservableObject<string>(); 

    var didrun = new DidRun(); 
    property.AddWeakPropertyChanged(
     (x) => 
     { 
      didrun.SetValue("Property Name = " + x); 
     }); 

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

    property.Value = "Hello World"; 

    Assert.IsTrue(DidRun.Value != null); 
} 
+0

vay, sayesinde bir çok ve gerçekten ı değerinde geçirilen işleyicisine sabit referans olduğu, ardı A ConditionalWeakTable bu için çalışması gerekir. Şimdi anladım. Yardımlarınız için tekrar teşekkürler! – MBoros

İlgili konular