2017-07-18 3 views
1

PropertyChangedEventManagerなどの弱いイベントマネージャを使用して、WPFアプリケーションのイベントを購読しています。WPFで「ハングアラウンド」のイベントが貧弱です - これは予期された動作ですか?

しかし、メモリリークの可能性がありますが、ガーベッジコレクタがイベントを受け取る前にイベントに反応するものだけが厳密にメモリリークではないと思います。ここに私の問題を実証する例を示します


MainWindowクラスと私が聞きたいことを、プロパティ番号とコードビハインド美しい、とリスニングクラスへの強い参照。

それは1を追加する/強い参照を削除し、1は、プロパティ番号変更するには、2つのボタンイベントがあります。XAMLで

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)); 
    } 
} 

を:

<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 

参照が除去された後、それはまた、かなりしばらくの間、トリガーます。しかし、が最終的に削除されているように見えますが、多分ガベージコレクションのラウンド後に真のリークは避けられますか?

これが期待どおりの動作ですか?私。弱いイベントリスナーは、イベントをトリガーする前に他の参照がないことを積極的にチェックするのではなく、オブジェクトがガベージコレクションされたことを単にチェックします(弱い参照だけなので起こります)。

通常はこれは問題ではありませんが、変更のためにかなり高価な操作(グラフのデータを取得)をしているケースがあります。トラック(ユーザーがレイアウト全体に物事をドラッグアンドドロップできる非常にインタラクティブなUI)。私は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

関連する問題