【问题标题】:What is the best practice to check if an object is changed?检查对象是否已更改的最佳做法是什么?
【发布时间】:2010-01-15 12:43:38
【问题描述】:

我需要知道如何检查对象是否已更改。基本上我需要一个名为 TrackChanges 的属性,当我将其设置为 true 一次并且如果此对象中的任何数据“更改”时,同一对象 (IsObjectChanged) 上的方法可以返回 true。

你曾经需要这样的东西吗?你是如何解决的?如果已经有这种情况的最佳实践,我不想发明轮子?

在我调用 TrackChange=true 之前,我正在考虑在它的 setter 中克隆该对象。当我通过使用反射调用 IsObjectChanged() 时,我会将它的所有公共字段值与克隆副本进行比较。我不确定这是否是一个好方法。

有什么建议吗?

谢谢, 布拉克·奥兹多安

【问题讨论】:

标签: c# .net


【解决方案1】:

当我需要跟踪对象属性的更改以进行测试时,我会在对象的 PropertyChanged 事件上挂接一个事件处理程序。这会对你有帮助吗?然后您的测试可以根据更改执行他们想要的任何操作。通常我会计算更改的数量,并将更改添加到字典等中。

要实现这一点,您的类必须实现INotifyPropertyChanged 接口。然后任何人都可以附加并收听更改的属性:

public class MyClass : INotifyPropertyChanged { ... }

[TestFixture]
public class MyTestClass
{
    private readonly Dictionary<string, int> _propertiesChanged = new Dictionary<string, int>();
    private int _eventCounter; 

    [Test]
    public void SomeTest()
    {
        // First attach to the object
        var myObj = new MyClass(); 
        myObj.PropertyChanged += SomeCustomEventHandler;
        myObj.DoSomething(); 
        // And here you can check whether the object updated properties - and which - 
        // dependent on what you do in SomeCustomEventHandler. 

        // E.g. that there are 2 changes - properties Id and Name changed once each: 
        Assert.AreEqual(2, _eventCounter); 
        Assert.AreEqual(1, _propertiesChanged["Id"]);
        Assert.AreEqual(1, _propertiesChanged["Name"]);
    }

    // In this example - counting total number of changes - and count pr property. 
    // Do whatever suits you. 
    private void SomeCustomEventHandler(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        var property = e.PropertyName;
        if (_propertiesChanged.ContainsKey(property))
            _propertiesChanged[property]++;
        else
            _propertiesChanged[property] = 1;

        _eventCounter++;
    }
}

【讨论】:

  • 不错! Bur 问题:1) 什么被认为是“改变”?在 msdn 中,它说它是在调用 setter 时抛出的。所以我仍然需要检查它是否与前面的值不同,对吧? 2)如果这个对象的一个​​属性是对另一种类型对象的引用,并且如果该对象的属性发生了变化,我认为情况会变得更加复杂。类似于:myPerson.Address.Ctiy = "aDifferentCityName"
  • 更改是 PropertyChanged 事件。实现 INotifyPropertyChanged 接口,您需要自己触发事件。当您自己控制它时,您基本上可以选择是否要将对相同值的更改视为更改。我没有。
  • 如果 MyObject 具有引用另一个对象的属性,并且您希望在该对象发生更改时发生 PropetyChanged 事件,MyObject 需要在其引用的对象上挂钩 PropertyChanged 事件并触发更改的事件以传递它。它肯定会变得更复杂 - 一开始可能很难,但当你理解它时它并不是那么复杂。祝你好运!
  • 专门针对您的示例 - 您的地址类也需要实现 INotifyPropertyChanged,并在其属性更改时触发事件。然后你有两个选择。要么通过 MyClass 直接绑定到 Address 对象,而 MyClass 不需要处理更改的事件。或者 - 如果它应该关心 - MyClass 需要将事件处理程序挂钩到其引用地址对象,并且当触发事件时,MyClass 上的事件处理程序应该为属性 Address 触发 PropertyChanged 事件 - 例如。但是属性地址的更改可能表示引用已更改。
【解决方案2】:

这有两个部分。更改通知的事件是一个部分,但维护历史记录是另一个重要部分。 Entity Framework 也这样做(就像 LINQ to SQL 一样),我也在自己的代码中实现了这一点。至少,您保留一个标志让成员说它已更改。根据您的要求,您也可以保留原始值。这通常成为单独对象的任务。实体框架将其更改跟踪保存在一个单独的对象中(EntityState,如果我没记错的话)。

在我自己的代码中,我开发了一个“DataMember”类,它不仅保存值,还保存更改标志、空状态和其他各种有用的东西。这些 DataMember 是 Entity 类中的私有成员,并且 Entity 提供了将数据公开为简单数据类型的属性。属性 get 和 set 方法与 DataMember 交互以“做正确的事”,但 DataMember 确实进行了更改跟踪。我的 Entity 类继承自“EntityBase”类,该类提供了在实体级别检查更改、接受更改(重置更改标志)等方法。添加更改通知将是我接下来要做的事情,但有一个 DataMember 个人类数据元素和拥有更改通知事件处理程序的 EntityBase 将大大简化此操作。

编辑添加:

现在我在工作,我可以添加一些代码示例。这是我的 DataMember 类的接口定义:

public interface IDataMember<T> : IDataMember
{
    T Value { get; set; }

    T Get();

    void Set(T value);
}

public interface IDataMember
{
    string FieldName { get; set; }
    string OracleName { get; set; }
    Type MemberType { get; }
    bool HasValue { get; set; }
    bool Changed { get; set; }
    bool NotNull { get; set; }
    bool PrimaryKey { get; set; }
    bool AutoIdentity { get; set; }
    EntityBase Entity { get; set;}

    object GetObjectValue();

    void SetNull();
}

这是实体类中的一个典型属性:

private DataMember<bool> m_Monday;

public bool? Monday
{
    get
    {
        if (m_Monday.HasValue)
            return m_Monday.Get();
        else
            return null;
    }
    set
    {
        if (value.HasValue)
            m_Monday.Set(value.Value);
        else
            m_Monday.SetNull();
    }
}

请注意,DataMember 可以支持可以为空的属性,也可以不支持。

添加DataMember的构造函数代码:

    m_Monday = new DataMember<bool>("Monday");
    Members.Add(m_Monday);

【讨论】:

    【解决方案3】:

    布拉克,

    您可以看看 Entity Framework 或 Microsoft 的其他框架。 您可以查看诸如 PropertyChanging 或 PropertyChanged 之类的事件。

    看看生成的代码。

    您也可以查看 NHibernate 代码,但由于代码库如此庞大,最好查看 Microsoft ORM 生成器..

    【讨论】:

      【解决方案4】:

      实现并使用接口INotifyPropertyChanged。没有字符串文字的一个很酷的方法是here

      【讨论】:

        【解决方案5】:

        你为什么不创建一个 List 并将你的第一个对象放入其中,然后你可以通过简单的比较将它与当前对象进行比较。

        如上所述,您可以使用 INotifyPropertyChanged 查看对象中的哪些属性发生了变化。

        【讨论】:

        • 简单的比较需要创建对象的(深层)副本。这在内存和时间上都是代价高昂的。
        【解决方案6】:

        您应该创建一个事件并将其命名为OnChanged,而不是创建一个属性。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-03-20
          • 2018-04-08
          • 2015-03-31
          • 1970-01-01
          • 1970-01-01
          • 2014-01-05
          • 2015-04-06
          相关资源
          最近更新 更多