5

你好 !

在这里,我有一个简单的类示例,其中包含 B 类类型的三个字段和其他一些内容。如您所见,我正在监听每个子对象的变化。因为我可能需要很多类型 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

我所知道的最优雅的方法是将面向方面编程 (AOP) 与PostSharp之类的工具结合使用。我在这里这里找到了 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

在这里可以找到一种简化属性设置的好方法。

关于您的级联通知:我猜您可以使用上面概述的方法来处理在INotifyPropertyChanged那里实现的属性的事件(取消)订阅。

于 2010-03-16T11:17:48.450 回答
0

为了简化一点,你可以做以下两件事。

首先,在 PropertyChanged 的​​处理程序中,第一个参数 sender 是触发事件的对象,至少如果您在 B 类中以与 A 类相同的方式实现 OnPropertyChanged。这意味着您只需要一个处理程序即可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 模板:(如果您想在 Visual Studio 中尝试此模板,请单击添加新项,选择类模板但将扩展名从 .cs 更改为 .tt,将以下源粘贴到.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 回答