如何最佳实践检查对象是否已更改?

23

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

您是否曾经需要过这样的东西,您是如何解决的? 如果已经有最佳实践适用于这种情况,我就不想重新发明轮子了。

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

有任何建议吗?

谢谢, burak ozdogan


可能是重复的问题:什么是判断对象是否被修改的最佳方法? - mathieu
6个回答

16

当我需要在测试中跟踪对象属性的更改时,我会在对象的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++;
    }
}

不错!但是有问题:1)什么被认为是“变化”?在 MSDN 中,它说当调用 setter 时会抛出异常。所以我仍然需要检查它是否与前一个值不同,对吗?2)如果此对象的属性是指向另一种类型对象的引用,并且如果该对象的属性发生了更改,那么情况就变得更加复杂了。类似于:myPerson.Address.Ctiy = "aDifferentCityName" - pencilCake
一个变化是一个PropertyChanged事件。实现INotifyPropertyChanged接口,你需要自己触发事件。由于你自己控制这个过程,所以你基本上可以选择是否将同一值的更改视为变化。但我不这样做。 - stiank81
如果MyObject有一个属性指向另一个对象,并且你希望在该对象更改时触发PropertyChanged事件,那么MyObject需要在其引用的对象上挂接一个PropertyChanged事件并触发一个更改事件来传递它。这确实变得更加复杂 - 起初可能很难,但是当你理解了它之后,它并不是真的那么复杂。祝好运! - stiank81
特别为您的例子 - 您的地址类需要实现INotifyPropertyChanged接口,并在其属性更改时触发事件。然后您有两个选择。要么通过MyClass直接绑定到Address对象,而且MyClass不需要处理更改事件。或者 - 如果它应该关心 - MyClass需要钩住一个事件处理程序到其引用地址对象,当事件被触发时,MyClass上的事件处理程序应该触发一个属性Address的PropertyChanged事件 - 例如。但是属性Address的更改可能意味着引用已更改。 - stiank81

4
有两部分内容。一部分是变更通知事件,另一部分是维护历史记录。Entity Framework 和 LINQ to SQL 都实现了这一点,我也在自己的代码中实现了。至少,您需要为成员保留一个标志来表示它已更改。根据您的要求,您可能还需要保留原始值。这通常成为一个单独的对象的任务。Entity Framework 在一个独立的对象(如果我记得正确的话是 EntityState)中保持其更改跟踪。
在我的代码中,我开发了一个“DataMember”类,不仅保存了值,还保持了更改标志、空状态和其他各种有用的东西。这些 DataMembers 是 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

Burak ,

你可以看一下微软的Entity Framework或其他框架。你会看到像PropertyChanging或PropertyChanged这样的事件。

看一下生成的代码。

你也可以看一下NHibernate代码,但由于代码库非常庞大,最好还是看看微软的ORM生成器。


2

1
为什么不创建一个列表,将第一个对象放入其中,然后使用简单的比较将其与当前对象进行比较。
如上所述,您可以使用INotifyPropertyChanged来查看对象中已更改的属性。

1
一个简单的比较需要创建一个(深度)复制你的对象。这既占用内存,也浪费时间。 - Dirk Vollmar

0

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


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