5

こんにちは !

ここに、型クラスBの3つのフィールドと他のいくつかのものを含む単純なクラスの例があります。ご覧のとおり、すべての子オブジェクトの変更をリッスンしています。型クラスBのプロパティがたくさん必要になる可能性があるので、コードを縮小する方法があるかどうか疑問に思います。リスナーとそれぞれのメソッドを作成すると、たくさんのコードがあるように見えます。辞書などを使用してこれを修正するにはどうすればよいですか?IoCでこれを修正できると言われましたが、どこから始めればよいかわかりません。

public class A : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public int _id;
    public int Id
    {
        get { return _id; }
        set
        {
            if (_id == value)
            {
                return;
            }

            _id = value;
            OnPropertyChanged("Id"); 
        }
    }

    public string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            if (_name == value)
            {
                return;
            }

            _name = value; 
            OnPropertyChanged("Name"); 
        }
    }

    public B _firstB;
    public B FirstB
    {
        get { return _firstB; }
        set 
        {
            if (_firstB == value)
            {
                return;
            }

            if (_firstB != null)
            {
                FirstB.PropertyChanged -= firstObjectB_Listener;
            }

            _firstB = value;

            if (_firstB != null) 
                FirstB.PropertyChanged += new PropertyChangedEventHandler(firstObjectB_Listener);

            OnPropertyChanged("FirstB"); 
        }
    }

    public B _secondB;
    public B SecondB
    {
        get { return _secondB; }
        set
        {
            if (_secondB == value)
            {
                return;
            }

            if (_secondB != null)
            {
                FirstB.PropertyChanged -= secondObjectB_Listener;
            }

            _secondB = value;

            if (_secondB != null)
                SecondB.PropertyChanged += new PropertyChangedEventHandler(secondObjectB_Listener);

            OnPropertyChanged("FirstB");
        }
    }

    public B _thirdB;
    public B ThirdB
    {
        get { return _thirdB; }
        set
        {
            if (_thirdB == value)
            {
                return;
            }

            if (_thirdB != null)
            {
                ThirdB.PropertyChanged -= thirdObjectB_Listener;
            }

            _thirdB = value;

            if (_thirdB != null)
                ThirdB.PropertyChanged += new PropertyChangedEventHandler(thirdObjectB_Listener);

            OnPropertyChanged("ThirdB");
        }
    }

    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }

    void firstObjectB_Listener(object sender, PropertyChangedEventArgs e)
    {
        Console.WriteLine("Object A has found a change of " + e.PropertyName + " on first object B");
    }

    void secondObjectB_Listener(object sender, PropertyChangedEventArgs e)
    {
        Console.WriteLine("Object A has found a change of " + e.PropertyName + " on second object B");
    }

    void thirdObjectB_Listener(object sender, PropertyChangedEventArgs e)
    {
        Console.WriteLine("Object A has found a change of " + e.PropertyName + " on third object B");
    }
}
4

5 に答える 5

2

私が知っている最も洗練された方法は、PostSharpなどのツールで Aspect Oriented Programming (AOP) を使用することです。INotifyPropertyChanged の実装例をこちらこちらで見つけました。これらにより、プロパティを属性で装飾することができ、PostSharp はコードのビルド時に INotifyPropertyChanged を実装します。

于 2010-03-16T12:20:11.437 に答える
1

依存関係チェーンを設定しているようです。AOP または静的解析ソリューションのいずれも、これを適切に処理することはできません。依存関係追跡を使用して実行時に依存関係チェーンを検出する Update Controls を確認してください。

あなたの例は次のようになります。

public class B
{
    private Independent<string> _someProperty = new Independent<string>();

    public string SomeProperty
    {
        get { return _someProperty; }
        set { _someProperty.Value = value; }
    }
}

public class A
{
    private Dependent<string> _dependentProperty;

    public A()
    {
        _dependentProperty = new Dependent<string>(() =>
            FirstB.SomeProperty + ", " + SecondB.SomeProperty + ", " + ThirdB.SomeProperty);
    }

    public string DependentProperty
    {
        get { return _dependentProperty; }
    }

    private Independent<int> _id = new Independent<int>();
    public int Id
    {
        get { return _id; }
        set { _id.Value = value; }
    }

    private Independent<string> _name = new Independent<string>();
    public string Name
    {
        get { return _name; }
        set { _name.Value = value; }
    }

    private Independent<B> _firstB = new Independent<B>();
    public B FirstB
    {
        get { return _firstB; }
        set { _firstB.Value = value; }
    }

    private Independent<B> _secondB = new Independent<B>();
    public B SecondB
    {
        get { return _secondB; }
        set { _secondB.Value = value; }
    }

    private Independent<B> _thirdB = new Independent<B>();
    public B ThirdB
    {
        get { return _thirdB; }
        set { _thirdB.Value = value; }
    }
}
于 2012-09-13T19:43:16.397 に答える
0

プロパティの設定を簡単にする良い方法の 1 つがここにあります

カスケード通知に関して: 上記のアプローチを使用して、そこに実装されているプロパティのイベントの (非) サブスクリプションを処理できると思いますINotifyPropertyChanged

于 2010-03-16T11:17:48.450 に答える
0

少し簡単にするために、次の 2 つのことを行うことができます。

まず、PropertyChanged のハンドラーで、最初のパラメーターである送信者は、少なくともクラス A と同じ方法でクラス B に OnPropertyChanged を実装した場合、イベントを発生させたオブジェクトです。これは、すべてに対して 1 つのハンドラーしか必要ないことを意味します。 B プロパティ。

private void BValueListener(object sender, PropertyChangedEventArgs e)
{
  Console.WriteLine("Found change of {0} on object {1}", e.PropertyName, sender);
}

どの B プロパティが送信したかを正確に知る必要がある場合は、BValueListener メソッドでチェックを行うことができます。

if (sender == FirstB) { /* Do special stuff here */ }

すべての B プロパティに対して同じリスナーを使用すると、次のようなプロパティ セッターを記述できます。

private B _thirdB;
public B ThirdB
{
  get { return _thirdB; }
  set {
    if (UpdateBValue(ref _thirdB, value)) {
      OnPropertyChanged("ThirdB");
    }
  }
}
private bool UpdateBValue(ref B value, B newValue)
{
  if (value == newValue)
  {
    return false;
  }

  if (value != null)
  {
    value.PropertyChanged -= BValueListener;
  }

  value = newValue;
  if (value != null)
  {
    value.PropertyChanged += BValueListener;
  }
  return true;
}

プロパティごとに異なるハンドラーが本当に必要な場合は、上記のコードを次のように変更できます。

private B _thirdB;
public B ThirdB
{
    get { return _thirdB; }
    set 
    { 
        if (UpdateBValue(ref _thirdB, value, BValueListener))
        {
            OnPropertyChanged("ThirdB");
        }
    }
} 

private bool UpdateBValue(ref B value, B newValue, PropertyChangedEventHandler eventHandler)
{
    if (value == newValue)
    {
        return false;
    }

    if (value != null)
    {
        value.PropertyChanged -= eventHandler;
    }

    value = newValue;

    if (value != null)
    {
        value.PropertyChanged += eventHandler;
    }
    return true;
}

それぞれの場合に使用したいリスナーメソッドを送信できます。

于 2010-03-16T12:08:01.287 に答える
0

検討できるツールは T4 です (T4 は VS2010 に同梱されているため、追加の依存関係は必要ありません)。

T4 は、「面倒な」コードのメンテナンスを減らすのに役立つコード生成ツールです。コード用の ASP/PHP を考えてみてください。XML/XSLT にも似ています。

T4 の優れた紹介については、 Oleg Sychsブログを参照してください。

このような場合のコード生成の利点は、生成されたコードが冗長であっても、管理するコード (T4 テンプレート) は冗長でないか、少なくとも冗長ではないことです。

あなたが提供したサンプルについて考えて、私はこの T4 テンプレートを書きました。 .tt ファイルと保存. 保存後、結果は対応する .cs ファイルにあるはずです)

// ReSharper disable InconsistentNaming
// ReSharper disable PartialMethodWithSinglePart
// ReSharper disable PartialTypeWithSinglePart
<#
    // This is the "model" that is "what" we would like to generate
    var classDefs = new []
    {
        new ClassDefinition
        {
            Name = "A",
            Properties = new []
            {
                P ("int"    , "Id"      ),
                P ("string" , "Name"    ),
                P ("B"      , "FirstB"  , listenToChanges:true  ),
                P ("B"      , "SecondB" , listenToChanges:true  ),
                P ("B"      , "ThirdB"  , listenToChanges:true  ),
            },
        },
        new ClassDefinition
        {
            Name = "B",
            Properties = new []
            {
                P ("int"    , "Id"      ),
                P ("string" , "Name"    ),
            },
        },
    };
#>

namespace SO
{
    using System;
    using System.ComponentModel;

<#
    // This part is the template ie "how" the model will be transformed into code
    foreach (var classDef in classDefs)
    {
#>        

    // ------------------------------------------------------------------------
    /// <summary>
    /// class <#=classDef.Name#> (implements INotifyPropertyChanged)
    /// </summary>
    public partial class <#=classDef.Name#> : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        void OnPropertyChanged (string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) 
            {
                handler (this, new PropertyChangedEventArgs (name));
            }
        }

<#
        foreach (var propertyDef in classDef.Properties)
        {
#>

        // --------------------------------------------------------------------
        /// <summary>
        /// Gets or sets property <#=propertyDef.Name#> (<#=propertyDef.Type#>)
        /// </summary>
        public <#=propertyDef.Type#> <#=propertyDef.Name#>
        { 
            get { return <#=propertyDef.FieldName#>; } 
            set 
            { 
                if (<#=propertyDef.FieldName#> == value) 
                { 
                    return; 
                } 

<#
            if (propertyDef.ListenToChanges)
            {
#>
                if (<#=propertyDef.FieldName#> != null) 
                { 
                    <#=propertyDef.FieldName#>.PropertyChanged -= <#=propertyDef.ListenerName#>;
                } 

                <#=propertyDef.FieldName#> = value; 

                if (<#=propertyDef.FieldName#> != null)  
                {
                    <#=propertyDef.FieldName#>.PropertyChanged += <#=propertyDef.ListenerName#>; 
                }
<#
            }
            else
            {
#>
                <#=propertyDef.FieldName#> = value; 
<#
            }
#>

                <#=propertyDef.EventName#> ();
                OnPropertyChanged("<#=propertyDef.Name#>");  

            } 
        } 
        // --------------------------------------------------------------------
        <#=propertyDef.Type#> <#=propertyDef.FieldName#>; 
        // --------------------------------------------------------------------
        partial void <#=propertyDef.EventName#> ();
        // --------------------------------------------------------------------
<#
            if (propertyDef.ListenToChanges)
            {
#>
        void <#=propertyDef.ListenerName#> (object sender, PropertyChangedEventArgs e)
        {
            Console.WriteLine (
                "Instance of <#=classDef.Name#> detected a change of <#=propertyDef.Name#>.{0}", 
                e.PropertyName
                );
            <#=propertyDef.EventName#> ();
        }
        // --------------------------------------------------------------------
<#
            }
        }
#>
    }
    // ------------------------------------------------------------------------
<#
    }
#>
}
<#+
    class ClassDefinition
    {
        public string Name;
        public PropertyDefinition[] Properties;
    }

    class PropertyDefinition
    {
        public string Type;
        public string Name;
        public bool ListenToChanges;

        public string FieldName 
        {
            get
            {
                return "_" + Name;
            }
        }

        public string ListenerName 
        {
            get
            {
                return Name + "_Listener";
            }
        }

        public string EventName 
        {
            get
            {
                return "Change_" + Name;
            }
        }
    }

    PropertyDefinition P (string type, string name, bool listenToChanges = false)
    {
        return new PropertyDefinition
            {
                Type = type ?? "<NO_TYPE>",
                Name = name ?? "<NO_NAME>",
                ListenToChanges = listenToChanges,
            };
    }
#>

最後に、これにより次の出力が生成されます。

// ReSharper disable InconsistentNaming
// ReSharper disable PartialMethodWithSinglePart
// ReSharper disable PartialTypeWithSinglePart

namespace SO
{
    using System;
    using System.ComponentModel;



    // ------------------------------------------------------------------------
    /// <summary>
    /// class A (implements INotifyPropertyChanged)
    /// </summary>
    public partial class A : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        void OnPropertyChanged (string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) 
            {
                handler (this, new PropertyChangedEventArgs (name));
            }
        }


        // --------------------------------------------------------------------
        /// <summary>
        /// Gets or sets property Id (int)
        /// </summary>
        public int Id
        { 
            get { return _Id; } 
            set 
            { 
                if (_Id == value) 
                { 
                    return; 
                } 

                _Id = value; 

                Change_Id ();
                OnPropertyChanged("Id");  

            } 
        } 
        // --------------------------------------------------------------------
        int _Id; 
        // --------------------------------------------------------------------
        partial void Change_Id ();
        // --------------------------------------------------------------------

        // --------------------------------------------------------------------
        /// <summary>
        /// Gets or sets property Name (string)
        /// </summary>
        public string Name
        { 
            get { return _Name; } 
            set 
            { 
                if (_Name == value) 
                { 
                    return; 
                } 

                _Name = value; 

                Change_Name ();
                OnPropertyChanged("Name");  

            } 
        } 
        // --------------------------------------------------------------------
        string _Name; 
        // --------------------------------------------------------------------
        partial void Change_Name ();
        // --------------------------------------------------------------------

        // --------------------------------------------------------------------
        /// <summary>
        /// Gets or sets property FirstB (B)
        /// </summary>
        public B FirstB
        { 
            get { return _FirstB; } 
            set 
            { 
                if (_FirstB == value) 
                { 
                    return; 
                } 

                if (_FirstB != null) 
                { 
                    _FirstB.PropertyChanged -= FirstB_Listener;
                } 

                _FirstB = value; 

                if (_FirstB != null)  
                {
                    _FirstB.PropertyChanged += FirstB_Listener; 
                }

                Change_FirstB ();
                OnPropertyChanged("FirstB");  

            } 
        } 
        // --------------------------------------------------------------------
        B _FirstB; 
        // --------------------------------------------------------------------
        partial void Change_FirstB ();
        // --------------------------------------------------------------------
        void FirstB_Listener (object sender, PropertyChangedEventArgs e)
        {
            Console.WriteLine (
                "Instance of A detected a change of FirstB.{0}", 
                e.PropertyName
                );
            Change_FirstB ();
        }
        // --------------------------------------------------------------------

        // --------------------------------------------------------------------
        /// <summary>
        /// Gets or sets property SecondB (B)
        /// </summary>
        public B SecondB
        { 
            get { return _SecondB; } 
            set 
            { 
                if (_SecondB == value) 
                { 
                    return; 
                } 

                if (_SecondB != null) 
                { 
                    _SecondB.PropertyChanged -= SecondB_Listener;
                } 

                _SecondB = value; 

                if (_SecondB != null)  
                {
                    _SecondB.PropertyChanged += SecondB_Listener; 
                }

                Change_SecondB ();
                OnPropertyChanged("SecondB");  

            } 
        } 
        // --------------------------------------------------------------------
        B _SecondB; 
        // --------------------------------------------------------------------
        partial void Change_SecondB ();
        // --------------------------------------------------------------------
        void SecondB_Listener (object sender, PropertyChangedEventArgs e)
        {
            Console.WriteLine (
                "Instance of A detected a change of SecondB.{0}", 
                e.PropertyName
                );
            Change_SecondB ();
        }
        // --------------------------------------------------------------------

        // --------------------------------------------------------------------
        /// <summary>
        /// Gets or sets property ThirdB (B)
        /// </summary>
        public B ThirdB
        { 
            get { return _ThirdB; } 
            set 
            { 
                if (_ThirdB == value) 
                { 
                    return; 
                } 

                if (_ThirdB != null) 
                { 
                    _ThirdB.PropertyChanged -= ThirdB_Listener;
                } 

                _ThirdB = value; 

                if (_ThirdB != null)  
                {
                    _ThirdB.PropertyChanged += ThirdB_Listener; 
                }

                Change_ThirdB ();
                OnPropertyChanged("ThirdB");  

            } 
        } 
        // --------------------------------------------------------------------
        B _ThirdB; 
        // --------------------------------------------------------------------
        partial void Change_ThirdB ();
        // --------------------------------------------------------------------
        void ThirdB_Listener (object sender, PropertyChangedEventArgs e)
        {
            Console.WriteLine (
                "Instance of A detected a change of ThirdB.{0}", 
                e.PropertyName
                );
            Change_ThirdB ();
        }
        // --------------------------------------------------------------------
    }
    // ------------------------------------------------------------------------


    // ------------------------------------------------------------------------
    /// <summary>
    /// class B (implements INotifyPropertyChanged)
    /// </summary>
    public partial class B : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        void OnPropertyChanged (string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) 
            {
                handler (this, new PropertyChangedEventArgs (name));
            }
        }


        // --------------------------------------------------------------------
        /// <summary>
        /// Gets or sets property Id (int)
        /// </summary>
        public int Id
        { 
            get { return _Id; } 
            set 
            { 
                if (_Id == value) 
                { 
                    return; 
                } 

                _Id = value; 

                Change_Id ();
                OnPropertyChanged("Id");  

            } 
        } 
        // --------------------------------------------------------------------
        int _Id; 
        // --------------------------------------------------------------------
        partial void Change_Id ();
        // --------------------------------------------------------------------

        // --------------------------------------------------------------------
        /// <summary>
        /// Gets or sets property Name (string)
        /// </summary>
        public string Name
        { 
            get { return _Name; } 
            set 
            { 
                if (_Name == value) 
                { 
                    return; 
                } 

                _Name = value; 

                Change_Name ();
                OnPropertyChanged("Name");  

            } 
        } 
        // --------------------------------------------------------------------
        string _Name; 
        // --------------------------------------------------------------------
        partial void Change_Name ();
        // --------------------------------------------------------------------
    }
    // ------------------------------------------------------------------------
}
于 2011-09-05T17:06:39.570 に答える