C# - 确定List <t>是否脏了?</t>

时间:2009-05-21 09:11:28

标签: c# compact-framework c#-2.0 hash generic-list

我正在序列化作为我的数据实体的类的列表。我有一个包含List的DataProvider。

我总是直接在集合中修改项目。

确定列表中的任何项目是否已更改的最佳方法是什么?我正在使用Compact Framework。

我唯一的想法是在加载列表时创建List的哈希值(如果可能的话)。然后,当我进行保存时,我重新获取列表的哈希值,看看它们是否是不同的值。如果它们不同,我保存然后更新存储的Hash以便稍后进行比较,如果它们是相同的则我不保存。

有什么想法吗?

7 个答案:

答案 0 :(得分:9)

如果您添加到列表中的项目实现了INotifyPropertyChanged界面,您可以构建自己的通用列表,在该界面中为您添加到列表中的所有对象挂钩事件,并在项目时取消挂钩事件从列表中删除。

您可以使用框架中的BindingList<T>类,或者您可以编写自己的类。

这是一个示例add方法,假设已使用where T: INotifyPropertyChanged声明类型:

public void Add(T item)
{
    // null-check omitted for simplicity
    item.PropertyChanged += ItemPropertyChanged;
    _List.Add(item);
}

this[index]索引器属性:

public T this[Int32 index]
{
    get { return _List[index]; }
    set {
        T oldItem = _List[index];
        _List[index] = value;
        if (oldItem != value)
        {
            if (oldItem != null)
                oldItem.PropertyChanged -= ItemPropertyChanged;
            if (value != null)
                value.PropertyChanged += ItemPropertyChanged;
        }
    }
}

如果您的商品不支持INotifyPropertyChanged,但他们是您的课程,我会考虑添加该支持。

答案 1 :(得分:4)

您可以创建自己的IList<T>课程,例如DirtyList<T>,可以记录列表更改的时间。

答案 2 :(得分:4)

如果您愿意使用反射,List<T>类有一个名为_version的私有字段,每次列表更改时都会增加。它不会告诉您哪些项目已更改,但您可以将其与_version的原始值进行比较,以检测未修改的列表。

作为参考,此字段用于确保在修改列表时枚举数变为无效。因此,除非List<T>的实际托管代码发生更改,否则您应该能够相当可靠地将其用于您的目的。

要获得_version的值,您可以使用以下内容:

List<T> myList;
var field = myList.GetType().GetField("_version", BindingFlags.Instance | BindingFlags.NonPublic);
int version = field.GetValue(myList);

一般来说,这不是最好的方法。但是,如果您使用其他人创建的List<T>卡住了,那么它可能是您拥有的最佳选择。请注意,对.NET框架的更改可能会更改字段的名称(或完全删除它),并且不能保证在Mono等第三方CLR实现中存在。

答案 3 :(得分:2)

这样的事情怎么样?

public class ItemChangedArgs<T> : EventArgs
{
    public int Index { get; set; }
    public T Item { get; set; }
}

public class EventList<T> : IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable
{
    private List<T> m_list;
    public event EventHandler<ItemChangedArgs<T>> ItemAdded;
    public event EventHandler<ItemChangedArgs<T>> ItemRemoved;
    public event EventHandler<ItemChangedArgs<T>> ItemChanged;
    public event EventHandler ListCleared;

    public EventList(IEnumerable<T> collection)
    {
        m_list = new List<T>(collection);
    }

    public EventList(int capacity)
    {
        m_list = new List<T>(capacity);
    }

    public EventList()
    {
        m_list = new List<T>();
    }

    public void Add(T item)
    {
        Add(item, true);
    }

    public void Add(T item, Boolean raiseEvent)
    {
        m_list.Add(item);
        if (raiseEvent) RaiseItemAdded(this.Count - 1, item);
    }

    public void AddRange(IEnumerable<T> collection)
    {
        foreach (T t in collection)
        {
            m_list.Add(t);
        }
    }

    private void RaiseItemAdded(int index, T item)
    {
        if (ItemAdded == null) return;

        ItemAdded(this, new ItemChangedArgs<T> { Index = index, Item = item });
    }

    public int IndexOf(T item)
    {
        return m_list.IndexOf(item);
    }

    public void Insert(int index, T item)
    {
        m_list.Insert(index, item);
        RaiseItemAdded(index, item);
    }

    public void RemoveAt(int index)
    {
        T item = m_list[index];
        m_list.RemoveAt(index);
        RaiseItemRemoved(index, item);
    }

    private void RaiseItemRemoved(int index, T item)
    {
        if(ItemRemoved == null) return;
        ItemRemoved(this, new ItemChangedArgs<T> { Index = index, Item = item });
    }

    public T this[int index]
    {
        get { return m_list[index]; }
        set 
        { 
            m_list[index] = value;
            RaiseItemChanged(index, m_list[index]);
        }
    }

    private void RaiseItemChanged(int index, T item)
    {
        if(ItemChanged == null) return;
        ItemChanged(this, new ItemChangedArgs<T> { Index = index, Item = item });
    }

    public void Clear()
    {
        m_list.Clear();
        RaiseListCleared();
    }

    private void RaiseListCleared()
    {
        if(ListCleared == null) return;
        ListCleared(this, null);
    }

    public bool Contains(T item)
    {
        return m_list.Contains(item);
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        m_list.CopyTo(array, arrayIndex);
    }

    public int Count
    {
        get { return m_list.Count; }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public bool Remove(T item)
    {
        for (int i = 0; i < m_list.Count; i++)
        {
            if(item.Equals(m_list[i]))
            {
                T value = m_list[i];
                m_list.RemoveAt(i);
                RaiseItemRemoved(i, value);
                return true;
            }
        }
        return false;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return m_list.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return m_list.GetEnumerator();
    }
}

答案 4 :(得分:2)

假设列表中包含的每个成员的GetHashCode()都正确实现(因此当元素发生变化时会发生变化)我会想到以下几点:

public class DirtyList<T> : List<T> {
    private IList<int> hashCodes = new List<int> hashCodes();
    public DirtyList() : base() { }
    public DirtyList(IEnumerable<T> items) : base() {
        foreach(T item in items){
            this.Add(item); //Add it to the collection
            hashCodes.Add(item.GetHashCode());
        }
    }

    public override void Add(T item){
        base.Add(item);
        hashCodes.Add(item);
    }
    //Add more logic for the setter and also handle the case where items are removed and indexes change and etc, also what happens in case of null values?

    public bool IsDirty {
       get {
           for(int i = 0; i < Count: i++){
               if(hashCodes[i] != this[i].GetHashCode()){ return true; }
           }
           return false;
       }
    }
}

*请注意我在SO上键入了这个并且没有编译器,所以上面提到的代码绝不会保证工作,但希望它能表明这个想法。

答案 5 :(得分:1)

您可以实现自己的列表,维护2个内部列表...以及实例化版本和跟踪版本......例如。

//Rough Psuedo Code
public class TrackedList<T> : List<T>
{
    public bool StartTracking {get; set; }
    private List<T> InitialList { get; set; }

    CTOR
    {
        //Instantiate Both Lists...
    }

    ADD(item)
    {
        if(!StartTracking)
        {
            Base.Add(item);
            InitialList.Add(item);
        }
        else
        {
            Base.Add(item);
        }
    }

    public bool IsDirty
    {
       get
       {
           Check if theres any differences between initial list and self.
       }
    }
}

答案 6 :(得分:1)

确保T是具有脏标志的对象的后代,并且IList实现检查了遍历列表脏标志的那个。