【问题标题】:Property Getters and Setters when implementing INotifyPropertyChanged?实现 INotifyPropertyChanged 时的属性获取器和设置器?
【发布时间】:2016-01-28 10:12:04
【问题描述】:

我正在尝试为很多类实现 INotifyPropertyChanged,并且这些类中的每一个都有很多很多属性。我一直在关注这个MSDN documentation 以了解如何实现 INofifyPropertyChanged,但是在类具有许多属性的情况下,它们的说明似乎并不实用。

目前我的大部分房产都使用速记:

public DateTime? DateClosed { get; set; }

但是文档说我需要在每个 setter 方法中添加以下内容:

// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("DateClosed");

这意味着我需要为 get 方法声明一个主体并声明私有变量来处理属性的实际获取和设置。像这样:

private DateTime? _dateOfIncident = null;
public DateTime? DateClosed
{
    get { return _dateOfIncident; }
    set
    {
        _dateOfIncident= value;
        // Call OnPropertyChanged whenever the property is updated
          OnPropertyChanged("DateClosed");
    }
}

有人知道解决这个问题的方法吗?

【问题讨论】:

  • 正则搜索替换,T4模板,代码sn-ps,你试过什么?我的意思是你不是第一个遇到这种情况的人。 :)
  • 您对正确的问题发表了评论吗?我不知道正则表达式和 T4 模板与这个大声笑有什么关系。我没有真正尝试过任何事情,因为除了 setter 之外,在其他任何地方调用 OnPropertyChanged() 似乎都没有意义。但同时我不想仅为 getter 和 setter 创建所有这些私有属性
  • @user3653277 是的,CodeCaster 评论了正确的问题。 T4 模板允许生成 C# 代码,并由 Visual Studio 编译生成的代码。正则表达式是生成 C# 代码的一种可能的简单方法。

标签: c# .net winforms inotifypropertychanged


【解决方案1】:

可以轻松更改一些类来实现INotifyPropertyChanged。但是由于您声明您有 很多 具有很多属性的类,因此手动甚至使用模板来完成这项工作是一个真正的负担。

你真正需要的是一个为你做这件事的工具,所以我向你介绍Fody,它是 NotifyPropertyChanged 插件。 Fody 所做的是在编译时在您的代码之间编织一些额外的代码。您唯一需要做的就是在您要实现的类上添加一个属性INotifyPropertyChanged,剩下的就为您完成了。

[ImplementPropertyChanged]
public class Person 
{        
    public string GivenNames { get; set; }
    public string FamilyName { get; set; }

    public string FullName
    {
        get
        {
            return string.Format("{0} {1}", GivenNames, FamilyName);
        }
    }
}

【讨论】:

    【解决方案2】:

    只需使用Observable Object 类。您无需创建 DateTime 属性,而是创建 ObservableObject<DateTime> 并绑定到 DateClosed.Value

    【讨论】:

    • 我认为这个答案应该指出该类不是 BCL 的一部分,而是带有 Prism
    【解决方案3】:

    我不确定您是否会在此处找到解决方法。自动属性,正如您现在使用的那样,实际上只是一种编译器简写,最终无论如何都会转换为带有支持字段的完整属性(至少,据我了解)。

    INPC 的使用是一种例行程序,它与正常财产的职责有些不同。它通知订阅者(通常是您的视图 XAML)相关属性已更改或正在更改。

    tl;dr -- 您不必将自动属性重写为带有支持字段的完整属性。但是像MVVMLight 这样的工具包有一些很棒的Visual Studio 代码sn-ps 可以使这个速度相对较快。最终你甚至可以这样做:

    private string _someString;
    public string SomeString
    {
        get { return _someString;}
        set 
        {
                //Set returns bool, so you can trigger other logic on it! 
                Set(() => SomeString, ref _someString, value);
        }
    }
    

    这为您提供了一些简洁的功能:

    • 强命名(与示例中的魔术字符串不同)
    • Set 仅在值不同时触发 INPC 事件
    • Set 返回布尔值,因此您可以在值更改时执行更多操作

    MVVMLight 很好,因为您不必使用它的所有功能,甚至不必实现 MVVM 模式。它只是有很多不错的“工具”可供您利用。

    【讨论】:

    • 当您在 Set 方法中使用 [CallerMemberName] 属性时,可以删除 () => SomeString 部分 - 它会导致编译器为您插入调用属性的名称。
    • 我看不出新属性比标准完整属性更好。你非常讨厌重复OnPropertyChanged()(和CallerMemberName ofc)来换取这种语法?
    • 对他的问题最简单的回答是:如果你想使用它,你需要在你的属性中实际实现 INPC。但我的回答指出,您不仅可以获得以 this 特定方式实现它的一些额外好处,而且 MVVMligth 包含的 coden-ps 使得实现许多属性变得轻而易举。
    【解决方案4】:

    有很多模式可以做,或者你可以买一个工具like PostSharp来帮你做。

    例如,这里有一种方法:

    public abstract class BaseNotifyPropertyChanged : INotifyPropertyChanged
    {
        private Dictionary<string, object> _valueStore = new Dictionary<string, object>();
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected T Get<T>([CallerMemberName]string property = null)
        {
            object value = null;
            if (!_valueStore.TryGetValue(property, out value))
                return default(T);
            return (T)value;
        }
    
        protected void Set<T>(T value, [CallerMemberName]string property = null)
        {
            _valueStore[property] = value;
            OnPropertyChangedInternal(property);
        }
    
        protected void OnPropertyChanged([CallerMemberName]string property = null)
        {
            OnPropertyChangedInternal(property);
        }
    
        private void OnPropertyChangedInternal(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    

    然后你从你的类继承:

    public class PlainOldObject : BaseNotifyPropertyChanged
    {
        public int MyProperty
        {
            get { return Get<int>(); }
            set { Set(value); }
        }
    }
    

    它会为您处理后备存储和一切。您可能希望添加逻辑以仅在属性实际更改时才调用 OnPropertyChangedInternal(比较引用或值),但我将把它留给您作为练习。

    【讨论】:

    • 字典查找而不是完整属性?代码 sn-ps 会做得更好(让您不必输入 2 行保存的代码)。
    • @Sinatr 我说这是一种方式,不是最佳方式,但即使使用 sn-ps 和一些性能问题,这确实具有触发通知的所有逻辑的优势一个地方,减少重复和维护。
    • “不是最佳方式”“一些性能问题” 是问题所在。至于“在一个地方触发通知的所有逻辑”所有逻辑都已经在OnPropertyChanged 中了。这是过度设计恕我直言,除非您必须做一些比触发事件更复杂的事情(想不出任何合适的事情)。
    • 对于它的价值,WPF 依赖属性基本上采用与此处所述相同的路径(减去通用类型安全)。
    猜你喜欢
    • 2014-07-24
    • 1970-01-01
    • 2014-03-11
    • 1970-01-01
    • 1970-01-01
    • 2011-08-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多