我正在序列化作为我的数据实体的类列表。我有一个包含列表的 DataProvider。
我总是直接在集合中修改项目。
确定列表中的任何项目是否已更改的最佳方法是什么?我正在使用紧凑型框架。
我目前唯一的想法是在加载列表时创建列表的哈希(如果可能的话)。然后,当我进行保存时,我重新获取列表的哈希值并查看它们是否是不同的值。如果它们不同,我保存然后更新存储的哈希以便稍后进行比较,如果它们相同,那么我不保存。
有任何想法吗?
我正在序列化作为我的数据实体的类列表。我有一个包含列表的 DataProvider。
我总是直接在集合中修改项目。
确定列表中的任何项目是否已更改的最佳方法是什么?我正在使用紧凑型框架。
我目前唯一的想法是在加载列表时创建列表的哈希(如果可能的话)。然后,当我进行保存时,我重新获取列表的哈希值并查看它们是否是不同的值。如果它们不同,我保存然后更新存储的哈希以便稍后进行比较,如果它们相同,那么我不保存。
有任何想法吗?
如果您添加到列表中的项目实现了该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
,但它们是您的类,我会考虑添加该支持。
您可以创建自己的IList<T>
课程,例如DirtyList<T>
可以记录列表何时更改。
如果您愿意使用反射,则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 实现中存在。
这样的事情怎么样?
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();
}
}
假设列表中包含的每个成员的 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 上输入了这个并且没有编译器,所以上述代码绝不保证工作,但希望它会显示这个想法。
你可以实现你自己的列表来维护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.
}
}
}
确保 T 是具有脏标志的对象的后代,并让 IList 实现检查遍历列表的脏标志的对象。