重构PropertyChangedEventHandler

4

在我的UI代码中,我有很多具有相同基本框架的类:

  • derives from INotifyPropertyChanged
  • contains the following code:

    void NotifyPropertyChanged(String info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
    
    public event PropertyChangedEventHandler PropertyChanged;
    

看起来这是一个很好的把代码重构到类中并派生出来的机会,而不是使用 INotifyPropertyChanged 接口,但遗憾的是 C# 不支持多重继承,所以这种方法并不能真正奏效。您有什么关于如何重构这种代码的想法吗?


让顶层父类从您的Notify类继承。 - Natrium
Natrium,这会让很多与此无关的类变得混乱。:( - Anders Rune Jensen
5个回答

1
也许你可以像这样使用:

class A1 : INotifyPropertyChanged
{
    private string _myProperty;
    private static Expression<Func<A1, string>> myProperty = _ => _.MyProperty;

    public string MyProperty
    {
        get { return _myProperty; }
        set
        {
            _myProperty = value;
            InvokePropertyChanged(myProperty);
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void InvokePropertyChanged<T>(Expression<Func<A1, T>> property)
    {
        PropertyChangedEventHandler Handler = PropertyChanged;
        if (Handler != null)
        {
            MemberExpression expression = (MemberExpression)property.Body;
            Handler(this, new PropertyChangedEventArgs(expression.Member.Name));
        }
    }
}

这将显著减少未来的代码更改;)

或者您可以使用Postsharp插件自动实现INotifyPropertyChanged


你能再加一些解释吗? - Anders Rune Jensen
Sergey,这是解决依赖于魔术字符串问题的众多好方案之一。不过我不确定这是否是原帖作者所询问的。 - Rob Fonseca-Ensor
@Anders:我的意思是,也许你可以手动完成它,而不是从某个类派生出来?如果不行,还有其他一些方法:1. 代码生成 2. 反射 3. PostSharp - Sergey Teplyakov
PropFu似乎没有解决问题,它仍然存在事件+方法重复的问题。不过它确实有一点帮助。 - Anders Rune Jensen
@Anders:没错!实际上,这段代码只是为进一步的重构提供了便利。你觉得PostSharp怎么样? - Sergey Teplyakov
PostSharp看起来很不错,但AOP能做的事情有限;-) - Anders Rune Jensen

1
一个常见的做法是有一个实现了INotifyPropertyChanged接口的基类,像这样:
public abstract class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;

        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

然后你的类将从这个类派生,当你想要通知属性值更改时,你可以调用OnPropertyChanged:

public class PersonViewModel : ViewModelBase
{

    public PersonViewModel(Person person)
    {
        this.person = person;
    }

    public string Name
    {
        get
        {
            return this.person.Name;
        }
        set
        {
            this.person.Name = value;
            OnPropertyChanged("Name");
        }
    }
}

1

你不能把这段代码放到你的超类的超类里吗?

  • Object
    • 你具体的NotifyPropertyChanged类 <-- 在这里插入
      • 无论你的视图模型从哪里继承(并阻止你使用多重继承)
        • 你具体的视图模型
        • 另一个具体的视图模型

大多数MVVM框架都为你提供了这样的类。

由于事件周围的访问规则,不幸的是你不能通过反射将其分解为扩展方法。


是的,我想我必须做类似这样的事情。感谢MVVM的参考,很高兴知道人们意识到这一点。我只是感到困惑,关于WPF的书籍(Programming WPF)没有提到任何类似的内容。或者说WPF没有一些notifypropertychanged类,因为很快就会发现它会导致丑陋的重复代码。 - Anders Rune Jensen
但正如我曾写给Natrium的那样,这确实是一个相当丑陋的解决方案,因为中间的类被涂抹成了一些它们实际上不应该关心的东西。我想这可能是面向对象编程可以做到的最好的事情了;-) - Anders Rune Jensen
嗯,你说的中间类被润滑到了不应该关心的事情上?在你的项目中,这些中间类负责什么? - Rob Fonseca-Ensor
中间的辅助类可能只有一些常见功能。这些功能不是真正与用户界面相关,现在它必须了解关于INotify的一些东西,这使得它变得不太通用。 - Anders Rune Jensen
也许可以将中间的辅助类推入一个单独的静态类、一组扩展方法或作为依赖项提供给您的 UI 类的辅助对象中?http://haacked.com/archive/2007/12/11/favor-composition-over-inheritance-and-other-pithy-catch-phrases.aspx - Rob Fonseca-Ensor

0

这可能只是一个小帮助,但您可以使用params关键字,以便一次更改多个属性。

    public event PropertyChangedEventHandler PropertyChanged;

    public void NotifyPropertiesChanged(params string[] Properties)
    {
        if (PropertyChanged != null)
            foreach (string property in Properties)
                PropertyChanged(this, new PropertyChangedEventArgs(property));
    }

这样做可以减少在未来通知属性更改时使用的代码行数。因此,您可以使用:

NotifyPropertiesChanged("foo", "bar");

而不是:

NotifyPropertyChanged("foo"); NotifyPropertyChanged("bar");

否则,我同意Anders的建议将其移到继承树上方可能是最好的选择。


0

这是四行代码,永远不会改变。创建一个片段!


1
尽管您可能需要添加线程安全性:void NotifyPropertyChanged(String info) { var handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(info)); } } - Rob Fonseca-Ensor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接