2017-07-18 69 views
1

我使用弱事件管理器(如PropertyChangedEventManager)來訂閱我的WPF應用程序中的事件。WPF中「徘徊」的弱事件 - 這是預期的行爲嗎?

但是,我發現了一個可能的內存泄漏 - 但我認爲它可能不是嚴格的內存泄漏,只是在垃圾收集器撿起它們之前對事件做出反應。這裏有一個例子展示我的問題:


MainWindow類和可愛的隱藏代碼與屬性號碼,我要聽,具有較強的參考監聽類。

它有兩個按鈕事件,一個添加/刪除強引用,一個改變的屬性號:

public partial class MainWindow : Window, INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 

    public MainWindow() 
    { 
     InitializeComponent(); 
     DataContext = this; 
    } 

    // strong reference to listening class that will be added/removed 
    private TestReferenceClass strongReference; 

    // property that the listening class will listen to 
    private int number; 
    public int Number 
    { 
     get { return number; } 
     set { number = value; NotifyPropertyChanged(); } 
    } 

    // for UI 
    private bool referenceExists; 
    public bool ReferenceExists 
    { 
     get { return referenceExists; } 
     set { referenceExists = value; NotifyPropertyChanged(); } 
    } 

    //button event to change Number 
    private void ChangeProp(object sender, RoutedEventArgs e) 
    { 
     Number++; 
    } 

    //button event to assign/remove reference 
    private void ToggleReference(object sender, RoutedEventArgs e) 
    { 
     if (strongReference != null) 
     { 
      strongReference = null; 
      ReferenceExists = false; 
     } 
     else 
     { 
      strongReference = new TestReferenceClass(this); 
      ReferenceExists = true; 
     } 
    } 

    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "") 
    { 
     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
    } 
} 

隨着XAML:

<Window x:Class="WpfApp.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
     mc:Ignorable="d" 
     Title="MainWindow" Height="150" Width="525"> 
    <Window.Resources> 
     <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/> 
    </Window.Resources> 
    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Orientation="Horizontal"> 
     <Button Margin="10, 0" Click="ToggleReference">Toggle Reff</Button> 
     <Grid> 
      <TextBlock Text="No Refference" Background="White" VerticalAlignment="Center"/> 
      <TextBlock Text="Refference" Visibility="{Binding ReferenceExists, Converter={StaticResource BooleanToVisibilityConverter}}" Background="White" VerticalAlignment="Center"/> 
     </Grid> 
     <Button Margin="10, 0" Click="ChangeProp">Change property</Button> 
     <TextBox Text="{Binding Number}" Width="200" IsReadOnly="True"/> 
    </StackPanel> 
</Window> 

然後是「引用」監聽屬性更改的類。這只不過是創建和使用弱事件偵聽器PropertyChangedEventManager有關數字的變化監聽:

public class TestReferenceClass 
{ 
    public TestReferenceClass(MainWindow source) 
    { 
     // add a weak event manager 
     PropertyChangedEventManager.AddHandler(source, Source_PropertyChanged, string.Empty); 
    } 

    private void Source_PropertyChanged(object sender, PropertyChangedEventArgs e) 
    { 
     if (e.PropertyName == "Number") 
     { 
      Console.WriteLine("Property changed! " + (sender as MainWindow).Number); 
     } 
    } 
} 

結果是一個小的應用程序,可以讓你創建和銷燬參考,並引發他們的活動,只留下弱聽者保持參照:

enter image description here

變得與它亂搞了一下,你可以觸發唯一正在舉行由弱引用的對象上的事件後明確。如果你幾次垃圾郵件的創建/銷燬按鈕就可以觸發多個事件爲相同的點擊這樣的:

Property changed! 3 
Property changed! 3 
Property changed! 3 
Property changed! 4 
Property changed! 4 
Property changed! 4 

參考被刪除後它也引發了相當長一段時間。但是,真正的泄漏是可以避免的,因爲它確實似乎最終被刪除,也許經過一輪垃圾收集?

這是預期的行爲?即弱事件監聽器在觸發事件之前沒有主動檢查沒有其他引用,而只是檢查該對象是否已被垃圾收集(可能發生這種情況,因爲它只是一個弱引用,所以最終會發生)?

通常情況下,這不會是一個問題,除非我有一個案例,我正在做一些非常昂貴的操作(獲取圖形的數據),因爲有一些變化,在一些不容易跟蹤(一個非常交互式的用戶界面,用戶可以在佈局上拖放東西)。我可以實現IDisposable或類似的,但它會是一個很大的重構。

回答

1

這是預期的行爲。當您使用弱事件模式訂閱事件時,事件處理將繼續進行,直到垃圾收集器收集聆聽者:https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/weak-event-patterns

+0

因此,一個解決方案是在觸發這個昂貴的更改之前強制垃圾收集,但不確定這是很好的做法。 – Joe

+0

不是。如果您想立即取消訂閱該事件,則應該使用強參考:https://stackoverflow.com/questions/43201093/is-it-safe-to-replace-all-standard-event-handler to-weakeventmanager-or-its-vari/43204926#43204926 – mm8

+0

我很害怕。謝謝。正確的,大的IDisposable重構時間! – Joe