只在基方法完成时调用派生方法

本文关键字:方法 调用 派生 完成时 | 更新日期: 2023-09-27 18:13:01

我觉得这是一个常见的场景,我很好奇是否有最佳实践解决方案。

像所有的WPF一样,我有一个方法在我的ViewModelBase上设置一个属性值并调用OnPropertyChanged:

protected virtual void SetProperty<T>(string propertyName, ref T oldValue, T newValue)
{
    if (Equals(oldValue, newValue)) return;
    oldValue = newValue;
    OnPropertyChanged(propertyName);
}

我有一个派生类SaveableViewModelBase,它做一些错误检查和其他事情,覆盖SetProperty:

protected override void SetProperty<T>(string propertyName, ref T oldValue, T newValue)
{
    base.SetProperty(propertyName, ref oldValue, newValue);
    ValidateProperty(propertyName, newValue);
}

我的问题是,如果Base过早返回,因为项目是相等的,我不想调用我的Derived代码。当然,我知道一些方法可以做到这一点:

  1. SetProperty可以返回一个bool, Derived检查(看起来很丑,因为这个方法的其他1000个用户不关心返回值)
  2. 重新检查Derived中的两个属性是否相等(简单,但不是DRY)

我总是喜欢保持DRY,我只是好奇是否有一个通用的解决方案,被认为是最佳实践?这似乎是整个框架中非常常见的场景。

只在基方法完成时调用派生方法

看起来像偏执,但如果它如此重要,也许你应该尽量不要在这种情况下使用继承,并创建基类与验证(也许,也为两个ViewModelBases抽象超类)

您可以在新的派生类中添加一个委托方法到onpropertychange,当onpropertychange被触发时,它将在这个新方法中运行ValidateProperty方法调用到事件。所以你可以在派生方法中删除Validateproperty方法,并且你不需要新的override方法,因为你可以在属性更改时触发Validateproperty。

一种选择是使用第二个函数来指定更改属性时需要发生的操作。在基类中定义一个不做任何事情的虚方法,在SetProperty中调用该方法,然后在子类中重写该虚方法。

在基类中你可以这样做:

protected virtual void SetProperty<T>(string propertyName, ref T oldValue, T newValue)
{
    if (Equals(oldValue, newValue)) return;
    oldValue = newValue;
    // Validate the property that was just changed.
    PropertyValidate(propertyName, ref oldValue, newValue);
    OnPropertyChanged(propertyName);
}
protected virtual void PropertyValidate<T>(string propertyName, ref T oldValue, T newValue)
{}

在子类中:

protected override void PropertyValidate<T>(string propertyName, ref T oldValue, T newValue)
{
    // Do some validation, adding some red marks to bad things.
}

另一个解决方案(我首先想到的那个过于复杂的解决方案)是回调。您可以给SetProperty<T>一个Action参数,并让覆盖SetProperty<T>将验证添加到Action中。比如:

protected virtual void SetProperty<T>(
    string propertyName,
    ref T oldValue,
    T newValue,
    Action onChanged = null)
{
    if (Equals(oldValue, newValue)) return;
    oldValue = newValue;
    onChanged?.Invoke(); // Invoke the onChanged action (if not null).
    OnPropertyChanged(propertyName);
}
protected override void SetProperty<T>(
    string propertyName,
    ref T oldValue,
    T newValue,
    Action onChanged = null)
{
    // Wrap the given onChanged callback with a new one that also validates the property.
    base.SetProperty(propertyName, ref oldValue, newValue,
        () => { onChanged?.Invoke(); ValidateProperty(propertyName, newValue); });
}