更优雅地处理属性更改事件侦听器(很多)(字典?)

发布于 2024-08-25 06:42:01 字数 3291 浏览 1 评论 0原文

你好 !

这里我有一个简单的类示例,其中包含三个 B 类类型的字段和一些其他内容。 正如你所看到的,我正在监听每个子对象的变化。 由于我可能需要 B 类类型的大量属性,我想知道是否有一种方法可以缩小代码。为每个创建一个侦听器+一个方法似乎我将有很多代码。我该如何解决这个问题......使用字典或类似的东西?我被告知国际奥委会可以解决这个问题,但我不知道从哪里开始。

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

hELLO !

Here i have a simple class example with three fields of type class B and some other stuff.
As you can see im listening on every child object change.
Since i could need alot of properties of type class B i wonder if there is a way of shrinking the code. Creating a listener + a method for each seems like i will have ALOT of code. How would i fix this ... using a dictionary or something similar? I have been told that IoC could fix this, but im not sure where to start.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

孤寂小茶 2024-09-01 06:42:01

我所知道的最优雅的方法是将面向方面编程(AOP)与诸如 PostSharp 之类的工具一起使用。我在此处此处。这些允许您使用属性来装饰您的属性,然后 PostSharp 在构建代码时为您实现 INotifyPropertyChanged。

The most elegant way I know of is to use Aspect Oriented Programming (AOP) with a tool such as PostSharp. I found INotifyPropertyChanged implementation examples here and here. These allow you to decorate your properties with an attribute and PostSharp then implements INotifyPropertyChanged for you when the code is built.

翻了热茶 2024-09-01 06:42:01

看起来您正在设置依赖链。 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; }
    }
}

It looks like you are setting up a dependency chain. None of the AOP or static analysis solutions are going to handle this properly. Check out Update Controls, which uses dependency tracking to discover dependency chains at runtime.

Here's what your example becomes:

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; }
    }
}
指尖上的星空 2024-09-01 06:42:01

可以在 此处找到一种简化属性设置的好方法

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

One nice way to simplify the setup of your properties can be found here.

Regarding to your cascading notifications: I'd guess that you could use the approach outlined above to handle the (un-)subscription of events for properties implementing INotifyPropertyChanged there.

箜明 2024-09-01 06:42:01

为了简化一点,您可以执行以下两件事。

首先,在 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;
}

可以在您想要的侦听器方法中发送的 位置在每种情况下使用。

To simplify at little bit you can do the following two things.

First, in the handler of the PropertyChanged the first parameter, sender, is the object that fired the event, at least if you have implmented the OnPropertyChanged in class B the same way as in class A. This means you only need one handler for all the B properties.

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

If you need to do know exactly which of the B properties did the sending you could do checks in the BValueListener method.

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

Having the same listener for all the B properties we can then move on to write the property setter like:

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

If you really need different handlers for each property you can modify the code above to something like

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

where you can send in the listener method you would like to use in each case.

述情 2024-09-01 06:42:01

您可以考虑的一个工具是 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 ();
        // --------------------------------------------------------------------
    }
    // ------------------------------------------------------------------------
}

A tool you could consider is T4 (T4 is shipped with VS2010 so no additional dependencies needed).

T4 is a code-generation tool which can help reduce the maintainance of "tedious" code. Think ASP/PHP for code. It's also similar to XML/XSLT.

For an excellent introduction to T4 please see Oleg Sychs blog.

The benefits of code-generation in a case like this is that even though the generated code is redundant the code that you maintain (the T4 template) isn't or at least is less redundant.

So thinking about the sample you provided I wrote this T4 template:
(If you would like to try this template in Visual Studio click Add New Item, chose class template but change the extension from .cs to .tt, paste the following source in the .tt file and save. After save the result should be in the corresponding .cs file)

// 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,
            };
    }
#>

Finally this produces this output:

// 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 ();
        // --------------------------------------------------------------------
    }
    // ------------------------------------------------------------------------
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文