【问题标题】:C# - Intercepting property changes in subclassesC# - 拦截子类中的属性更改
【发布时间】:2011-05-21 00:41:34
【问题描述】:

我正在创建一个框架,我在其中提供基类,框架的实现者将从基类继承并提供额外的属性和方法。在基类中,我想有一种方法来观察属性值何时发生变化。该属性可以来自基类或任何子类。我知道通过反射,我可以确定任何实例的属性列表,但是有没有办法可以跟踪属性变化值?

这是我所说的一个非常简单的例子:

public class BaseClass
{
    public string BaseClassProperty { get; set; }

    public void DoSomethingWhenEitherPropertyGetsChanged()
    {

    }
}

public class SubClass : BaseClass
{
    public string SubClassProperty { get; set; }
}

当任一属性的值发生更改时,我该怎么做才能让 DoSomethingWhenEitherPropertyGetsChanged 执行。

【问题讨论】:

  • 即使在基类中,您也必须在属性设置器中调用DoSomethingWhenEitherPropertyGetsChanged(),以便您可以在子类的属性设置器中执行相同的调用。
  • 对于任何对实现拦截器感兴趣的人,请查看TinyInterceptor

标签: c# .net c#-4.0 .net-4.0


【解决方案1】:

您可以为此目的使用notifypropertyweaver。它完全符合您的要求。这是一个链接:

来自开源主页:

使用 IL 编织(通过 http://www.mono-project.com/Cecil)将 INotifyPropertyChanged 代码注入属性。

  • 不需要属性
  • 无需参考
  • 不需要基类
  • 支持 .net 3.5、.net 4、Silverlight 3、Silverlight 4、Silverlight 5 和 Windows Phone 7
  • 支持客户端配置文件模式

【讨论】:

  • 谢谢!经过快速测试,这似乎正是我所需要的!
【解决方案2】:

我可能会使用 Postsharp 并创建一个继承属性,将拦截代码注入所有公共属性。将属性标记为继承也应该自动将其附加到所有子类。

【讨论】:

  • 您是说应该在BaseClass 上创建一个属性,该属性将拦截基类以及任何继承类的所有属性的setter 方法?
  • Postsharp 定义了一些属性,基本上可以让你将代码注入到各种方法中。
【解决方案3】:

我根据您的要求编写了自己的想法,但我不确定它是否适合您的需求。 INotifyProperty changed 是你也可以研究的,但我不太喜欢它,因为它就像连接意大利面条。不过,也许这会给你一些创意。

这样做的目的是允许您将 ObservableObject 用作​​所有属性类型。通过这样做,每个属性都会有一个可以连接到的 ObjectChanged 事件。缺点是您必须在构造函数中初始化所有属性,以防止代码中某处出现 NullReferenceException。

这个例子使用了三个类。

  • ObservableObject.cs
  • Employee.cs
  • Program.cs

ObservableObject.cs

    //-----------------------------------------------------------------------------
    // <copyright file="ObservableObject.cs" company="DCOM Productions">
    //     Copyright (c) DCOM Productions.  All rights reserved.
    // </copyright>
    //-----------------------------------------------------------------------------

    namespace PropertyChangedEventExample {
        using System;

        public class ObservableObject : Object {
            /// <summary>
            /// Expose the default constructor
            /// </summary>
            public ObservableObject() {
                // No default implementation
            }

            private object m_Object = null;
            /// <summary>
            /// Base object
            /// </summary>
            public object Object {
                get {
                    return m_Object;
                }
                set {
                    if (m_Object != value) {
                        m_Object = value;
                        OnObjectChanged(this, EventArgs.Empty);
                    }
                }
            }

            /// <summary>
            /// Triggered when the value of this object has changed.
            /// </summary>
            public event System.EventHandler<EventArgs> ObjectChanged;
            /// <summary>
            /// EventHandler wire-up
            /// </summary>
            protected virtual void OnObjectChanged(object sender, System.EventArgs e) {
                if (ObjectChanged != null) {
                    ObjectChanged(sender, e);
                }
            }

            /// <summary>
            /// Gets the value
            /// </summary>
            public object Get() {
                return this.Object;
            }

            /// <summary>
            /// Sets the value
            /// </summary>
            public void Set(object value) {
                this.Object = value;
            }
        }
    }

Employee.cs

    //-----------------------------------------------------------------------------
    // <copyright file="Employee.cs" company="DCOM Productions">
    //     Copyright (c) DCOM Productions.  All rights reserved.
    // </copyright>
    //-----------------------------------------------------------------------------

    namespace PropertyChangedEventExample {
        using System;

        public class Employee {
            /// <summary>
            /// Expose default constructor
            /// </summary>
            public Employee() {
                Name = new ObservableObject();
            }

            /// <summary>
            /// Gets or sets the name
            /// </summary>
            public ObservableObject Name {
                get;
                set;
            }
        }
    }

Program.cs

    //-----------------------------------------------------------------------------
    // <copyright file="Program.cs" company="DCOM Productions">
    //     Copyright (c) DCOM Productions.  All rights reserved.
    // </copyright>
    //-----------------------------------------------------------------------------

    namespace PropertyChangedEventExample {
        using System;

        class Program {
            static void Main(string[] args) {
                Employee employee = new Employee();
                employee.Name.Set("David");
                employee.Name.ObjectChanged += new EventHandler<EventArgs>(Name_ObjectChanged);
                employee.Name.Set("Dave");
                Console.ReadKey(true);
            }

            static void Name_ObjectChanged(object sender, EventArgs e) {
                ObservableObject employee = sender as ObservableObject;
                Console.WriteLine("Name changed to {0}", employee.Get());
            }
        }
    }

【讨论】:

    【解决方案4】:

    您最好的选择是 CrisWue 推荐的方法,并使用 postsharp 或其他一些后处理器将行为注入您的属性中。除此之外,我认为您需要在属性中手动调用 DoSomethingWhenEitherPropertyGetsChanged()。

    如果您正在创建一个供您或您的组织以外的人使用的库,则后处理器可能不是正确的方法,因为它会将 3rd 方工具作为另一个要求添加到他们的构建过程中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-15
      • 1970-01-01
      • 2015-04-29
      • 1970-01-01
      • 2016-12-03
      相关资源
      最近更新 更多