1

次のコードがあります。

したがって、基本的には、イベントが発生DelegateCommandしたときにコマンド (弱参照デリゲートに基づく) を実行します。Selector.SelectionChanged

    public static readonly DependencyProperty SelectionCommandProperty
        = DependencyProperty.RegisterAttached(
            "SelectionCommand",
            typeof(ICommand),
            typeof(CommonUtilities),
            new PropertyMetadata(null, OnSelectionCommandPropertyChanged));

    private static void OnSelectionCommandPropertyChanged(
        DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var selector = d as Selector;
        var command = e.NewValue as ICommand;
        if (selector != null && command != null)
        {
            selector.SelectionChanged
                += (o, args) => command.Execute(selector.SelectedItem);
        }
    }

    public static ICommand GetSelectionCommand(DependencyObject d)
    {
        return d.GetValue(SelectionCommandProperty) as ICommand;
    }

    public static void SetSelectionCommand(DependencyObject d, ICommand value)
    {
        d.SetValue(SelectionCommandProperty, value);
    }

コンテキストは静的であることに注意してください。

これは漏れの原因になりますか?私の知る限り、すべての「外部」変数 (つまりselectorcommandここ) のスコープが GC に適用されなくなるまで、匿名ハンドラーが有効になるため、そうではないと推測できます。View(それは を持っているselector) とViewModel(それは を供給している) が親 GUI からアンロードされたときに発生する GC が行われるとcommand、匿名デリゲートもフック解除されます。

私はここにいますか?

4

2 に答える 2

2

この例の参照は次のとおりです。

  • 意見:
    • セレクタ
  • ViewModel:
    • ICommand
  • セレクタ:
    • 匿名のデリゲート
    • ICommand
  • 匿名のデリゲート:
    • セレクタ
    • ICommand

これは、ビューとビューモデルをガベージコレクションして、SelectorとをICommand存続させることができることを意味します。

ガベージコレクターは循環参照を処理できます。したがってSelector、デリゲートを参照し、デリゲートがこれらを参照してSelectorいる場合でも、これらはガベージコレクションされる可能性があります。

ただし、このICommand匿名のデリゲートが存続している限り、存続し続けます。これは、Selectorインスタンスの存続期間によってのみ決定されます。がガベージコレクションされている限りSelector、デリゲートとICommand最終的にはガベージコレクションも行われます。

したがって、単純な状況では、いいえ、コードによってリークが発生することはありません。

ただし、コードがハンドラーをリークする状況があります。ビューモデルには次のようなプロパティがあると想定しています。

public ICommand OnSelectionChanged
{
    get { return _onSelectionChanged; }
    private set 
    { 
        _onSelectionChanged = value;
        RaisePropertyChanged("OnSelectionChanged");
    }
}

次にビューにバインドされます。このコマンドの値を変更OnSelectionChangedすると、古いコマンドを実行するデリゲートのサブスクライブを解除しないため、アタッチされたプロパティはイベントハンドラーをリークします。

したがって、1つのコマンドだけが実行されるのではなく、このプロパティの以前のすべての値が実行されます。

私は次のような実装に行きます:

private static void OnSelectionCommandPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var selector = d as Selector;

    if (selector != null)
    {
        var oldCommand = e.OldValue as ICommand;
        var newCommand = e.NewValue as ICommand;
        if(oldCommand == null && newCommand != null)
        {
            selector.SelectionChanged += OnSelectionChanged;
        }
        else
        {
            selector.SelectionChanged -= OnSelectionChanged;
        }
    }
}

private static void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    var selector = (Selector)sender;
    var command = GetSelectionCommand(selector);
    if(command != null)
    {
        command.Execute(selector.SelectedItem);
    }
}
于 2012-12-13T13:13:11.800 に答える
0

匿名ハンドラーの有効期間は、外部変数ではなくセレクターオブジェクトに厳密に依存するため、サブスクライブを解除するか、セレクターオブジェクトがガベージコレクションされるまで存在します。したがって、この方法では、メモリリークを引き起こすことはありません。

同じオブジェクトに対して複数のサブスクリプションが存在する可能性があるため、ある時点でサブスクリプションを解除する必要がある場合があることに注意してください。

于 2012-12-13T13:12:31.873 に答える