【问题标题】:How to check if an object has changed?如何检查对象是否已更改?
【发布时间】:2011-10-31 10:44:13
【问题描述】:

我有一个对象,其中包含其他对象,其中包含其他对象,包括列表等。此对象数据绑定到表单,在不同的选项卡中向用户公开大量字段。 我也使用主子数据网格视图。

知道如何检查该对象相对于较早时刻是否有任何变化吗?无需(手动)添加更改的变量,该变量在所有 (>100) 组方法中设置为 true。

【问题讨论】:

  • 只是好奇,但你为什么要这个。如果您的对象数据绑定正确,您的表单应该相应更新。
  • Objects 在这里指的是什么。您是指您从不同类型声明的不同组件或对象吗?有点混乱。
  • 你是什么意思'没有(手动)添加一个改变的变量'你不想写代码来解决这个问题?
  • 对象可以是简单的对象,例如双精度或字符串,也可以是自定义对象的列表,其中可能包含其他对象的列表。
  • 将您自己的自定义事件写入并触发您是 Obejct 的访问者,例如 set { name_ = value; //火灾事件};

标签: c#


【解决方案1】:

正如 Sll 所说,脏接口绝对是一个好方法。更进一步,我们希望集合是脏的,但我们并不一定要将所有子对象都设置为脏的。然而,我们可以做的是将它们的脏状态的结果与我们的自己的脏状态结合起来。因为我们使用的是接口,所以我们将其留给对象来确定它们是否脏。

我的解决方案不会告诉你什么是脏的,只是任何对象在任何时候的状态都是脏的。

public interface IDirty
{
    bool IsDirty { get; }
}   // eo interface IDirty


public class SomeObject : IDirty
{
    private string name_;
    private bool dirty_;

    public string Name
    {
        get { return name_; }
        set { name_ = value; dirty_ = true; }
    }
    public bool IsDirty { get { return dirty_; } }
}   // eo class SomeObject


public class SomeObjectWithChildren : IDirty
{
    private int averageGrades_;
    private bool dirty_;
    private List<IDirty> children_ = new List<IDirty>();

    public bool IsDirty
    {
        get
        {
            bool ret = dirty_;
            foreach (IDirty child in children_)
                dirty_ |= child.IsDirty;
            return ret;
        }
    }

}   // eo class SomeObjectWithChildren

【讨论】:

  • 我喜欢你的建议,但我可能会在属性访问器的 SetChange 上触发一个事件行。
【解决方案2】:

您可以实现 INotifyPropertyChanged 接口,如果您使用 VS2010,则有一个插件可以自动更改 IL 中的所有属性(因此您不必手动实现它)。

我相信还有其他一些使用编织技术的方法。

我在 vs2010 库中找到了插件:

http://visualstudiogallery.msdn.microsoft.com/bd351303-db8c-4771-9b22-5e51524fccd3

有一个很好的例子 - 你的代码:

public class Person : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public string GivenNames { get; set; }
}

编译的内容:

public class Person : INotifyPropertyChanged
{

    public event PropertyChangedEventHandler PropertyChanged;

    private string givenNames;
    public string GivenNames
    {
        get { return givenNames; }
        set
        {
            if (value != givenNames)
            {
                givenNames = value;
                OnPropertyChanged("GivenNames");
                OnPropertyChanged("FullName");
            }
        }
    }
}

这是来自 unce G 的第一个结果(可能有用):

http://justinangel.net/AutomagicallyImplementingINotifyPropertyChanged

http://www.codeproject.com/KB/WPF/AutonotifyPropertyChange.aspx

【讨论】:

  • 我个人不会使用 INotifyPropertyChanged (pub/sub) 来遍历父图,因为遍历父图是按需的,并且可以推送到并行或后台任务。事件模型往往会在很多人开发它们的大型应用程序中引发事件风暴。
【解决方案3】:

您如何定义“平等”(在新旧状态之间)?

  • 您是仅比较属性还是同时比较字段?
  • 您是否只比较 public 属性/字段?
  • 您是否曾经忽略任何属性/字段(即它们的修改无关紧要)?
  • 如何比较“原子”类型(例如,所有字符串比较是否不区分大小写,或者您在某些地方也需要区分大小写)。

如果这些问题的答案足够笼统(即您可以设计一套适用于所有对象的规则),那么理论上您可以通过反思实现您想要的:基本思想是读取“根”对象的所有属性/字段,然后存储“原子”对象并递归下降到“非原子”对象(并重复整个过程)。稍后,当您想检查是否有任何变化时,您将重复递归下降并将结果与​​存储的值进行比较。

我并不是说这个解决方案特别高效甚至简单(你需要设计一个健壮的命名约定来存储旧值并且非常小心多线程),但它可能是通用的.

【讨论】:

  • 我喜欢反射的想法,但我不想自己实现它。我可以哄 XML 序列化程序检查是否相等吗?还是它会重新安排事情?
【解决方案4】:

您可以覆盖GetHashCode,然后创建一个混合了对象属性的哈希码。 因此您的程序将获取对象的哈希码,将其存储,然后在下次检查时将其与当前哈希码进行比较。

一个非常简单的方法:

internal class Foo
{
    public string Bar { get; set; }
    public int Baaz { get; set; }

    public override int GetHashCode()
    {
        return Bar.GetHashCode() - Baaz.GetHashCode();
    }
}

小心,因为您必须寻找 Bar 而不是 null 并且还要满足整数溢出。


编辑

查看 Eirc Lippert 的 blog 后,

我同意不能使用 GetHashCode。

我保留答案以保留丰富的讨论。

【讨论】:

  • ^ 这应该注意与实现更改的哈希码一起出现的警告。就像添加 IsDirty 标志一样容易。
  • 不,不是。 IsDirty 必须在类属性的所有设置器和该类中使用的所有类上设置。 Urrrgh,..然后你什么时候重置它?假设它改变了,然后你想知道它是否再次改变?
  • 是的,它需要一定的努力,并且可能需要一些基础设施(如果您的要求要求的话),但它在不使用 GetHashCode 的情况下工作,其方式会过载(并且可能会破坏)它预期用途。
  • 使用哈希码来做这件事不是一个好主意。首先,您也必须重写 Equals() 方法。其次,对象的哈希值在对象的生命周期内永远不应该改变(也意味着它在重要的地方应该是不可变的)。您出于错误的原因使用现有方法只是为了保存一个单独的方法。
【解决方案5】:

随着时间的推移比较哈希码可能是一种选择。如果您不想添加该逻辑,则可以将对象序列化两次并比较两个结果字符串的哈希码。

编辑以包含一些 cmets 看看这个问题/答案:Can serializing the same object produce different streams?

所以请注意一个序列化程序,它**不**保证同一对象两次输出相同。

【讨论】:

  • 具有完全相同值的两个对象的序列化流不保证相同。 我亲眼所见。
  • @Ritch 别管哈希码,流不保证是一样的。
  • 我猜这取决于序列化的类型。这不是关于流是否相等,而是关于格式化程序创建相同的“事物”/结构/字符串
  • @Aliostad - 我不买那个。你是说如果我把同一个对象通过序列化器,它不会产生相同的结果?
【解决方案6】:

如果您想要这种懒惰且简单的方法,那么@Burimi 是正确的,因为您需要在您的类设置器和获取器中管理它。

你将不得不原谅这个表达,但是,“老派”你的类/对象属性回到拥有一个访问私有变量的公共 getter 和 setter。在公共设置器中,您可以设置对象的脏标志以及私有变量...但是您必须为每个重要的属性一个一个地做...我个人认为这是其中之一未来:

public string FirstName { get; set => { this.isDirty=true; } }

这基本上等同于当前的 { get; set; } ,而且,如果其中任何一个旁边有一个 =&gt; ,它将等同于 and run this code at after //getting or setting it 但我什至不知道这是否是现在人们想拥有或不想拥有的东西。

public class myObject
{
    public bool isDirty { get; set; }
    private string FirstName_ = "";
    public string FirstName { get { return FirstName_; } set { FirstName_ = value; isDirty = true; } }
}

现在,无论何时更改 FirstName_,整个对象都会变脏...重复冲洗。

当然,如果您还想报告,那么您所要做的就是触发您自己创建并拥有您需要的数据的通知事件......这是我的意思的一个例子:

public class myObject
{
    public bool isDirty { get; set; }
    private string FirstName_ = "";
    public string FirstName { get { return FirstName_; } set { FirstName_ = value; isDirty = true; RaisePropertyChanged("FirstName"); } }

    #region use YOUR_OWN_NOTIFY_EventHandler

    public event YOUR_OWN_NOTIFY_EventHandler PropertyChanged;

    public void RaisePropertyChanged(string propertyName)
    {
        YOUR_OWN_NOTIFY_EventTHandler handler = PropertyChanged;
        if (handler != null) handler(new myPropertyChangedEventArgs(propertyName));
    }

    #endregion
}

希望这对某人有所帮助...

【讨论】:

    【解决方案7】:

    我可以这样建议:​​

    public class ObjectBase : INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected virtual void OnPropertyChanged(string propertyName)
            {
                OnPropertyChanged(propertyName, true);
            }
    
            protected virtual void OnPropertyChanged(string propertyName, bool makeDirty)
            {
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    
                if (makeDirty)
                    _IsDirty = true;
            }
    
    
            bool _IsDirty;
    
            public bool IsDirty
            {
                get { return _IsDirty; }
            }
    
        }
    

    然后在属性访问器中你可以

    public class Vehicle : ObjectBase
    {
    
    
      int _CarId;
    
     public int CarId
            {
                get { return _CarId; }
                set
                {
                    if (_CarId != value)
                    {
                        _CarId = value;
                        OnPropertyChanged(nameof(CarId));
                    }
    
                }
            }
      }
    

    希望对你有帮助

    【讨论】:

    • 不确定这将如何工作。除了仅在实例化时设置的属性之外,我如何判断一个属性是否实际上是脏(更改)?
    猜你喜欢
    • 1970-01-01
    • 2011-10-10
    • 2017-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-17
    • 1970-01-01
    相关资源
    最近更新 更多