2009-05-21 9 views
4

Ich bin Serialisierung Listen von Klassen, die meine Datenentitäten sind. Ich habe einen DataProvider, der eine Liste enthält.C# - Ermitteln, ob die Liste <T> verschmutzt ist?

Ich modifiziere immer Elemente direkt in der Sammlung.

Wie kann man am besten feststellen, ob sich Elemente in der Liste geändert haben? Ich benutze das Compact Framework.

Meine einzige aktuelle Idee ist es, einen Hash der Liste zu erstellen (wenn das möglich ist), wenn ich die Liste lade. Dann, wenn ich einen Speichervorgang mache, erhalte ich den Hash der Liste und sehe, ob sie unterschiedliche Werte haben. Wenn sie anders sind, speichere ich und speichere den gespeicherten Hash später zum Vergleich, wenn sie gleich sind, dann speichere ich nicht.

Irgendwelche Ideen?

+0

Ich würde Hash-Dings Idee verwenden ... –

+0

Wie würde ich meine Hashing-Idee tun? – GenericTypeTea

+0

Sie müssen daran denken, dass sich die Liste nicht ändert, wenn Sie eine Eigenschaft eines Elements in der Liste ändern. Ich würde mit Lassevks Vorschlag gehen. – Onots

Antwort

9

Wenn die Elemente, die Sie der Liste hinzufügen, die Schnittstelle INotifyPropertyChanged implementieren, können Sie Ihre eigene generische Liste erstellen, die das Ereignis in dieser Schnittstelle für alle Objekte anhängt, die Sie der Liste hinzufügen, und das Ereignis aufhebt, wenn die Elemente entfernt werden Die Liste.

Es gibt eine BindingList<T> Klasse im Framework, die Sie verwenden können, oder Sie können Ihre eigenen schreiben.

Hier ist ein Beispiel add-Methode, vorausgesetzt, der Typ mit where T: INotifyPropertyChanged erklärt wurde:

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

und die this[index] Indexer Eigenschaft:

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; 
     } 
    } 
} 

Wenn Ihre Einzelteile nicht unterstützt INotifyPropertyChanged, aber sie In deinen Klassen würde ich darüber nachdenken, diese Unterstützung hinzuzufügen.

+0

Nur ein Jahr später ... entschied ich, dass dies eine bessere Antwort ist als das, was ich vor 12 Monaten herausgefunden habe. – GenericTypeTea

4

Sie könnten Ihre eigene IList<T>-Klasse erstellen, z. B. DirtyList<T>, die aufzeichnen kann, wenn sich die Liste geändert hat.

+0

Wie würde ich das machen? Ich hatte diese Idee und verwarf sie, weil ich nicht wusste, wie ich herausfinden würde, ob ein Gegenstand in der Liste geändert wurde, zB Item [0] .Id = NewId(), wie würde meine Liste lauten Wissen Sie, dass sich der Artikel geändert hat? – GenericTypeTea

+3

One-Way - überschreiben Methoden und Requisiten, die die Liste ändern (d. H. Hinzufügen) ändern Sie Ihre booleschen Flag dort und rufen Sie dann die Basis-Methode. – annakata

+0

Wenn ich ihn richtig verstehe, macht er Dinge wie diese: list [1] .Prop = newValue; Wenn das Element in Index 1 in der Liste die Liste nicht informiert (und somit weiß, dass es in einer Liste ist), hat diese Anweisung dieses schmutzige Flag nicht gesetzt, es sei denn, Sie markieren jeden dieser [Index] Aufruf als schmutzig –

1

Sie könnten Ihre eigene Liste implementieren, die 2 interne Listen verwaltet ... und instanziierte Version und Tracking-Version ... z.

//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. 
     } 
    } 
} 
+0

Leider kann ich das nicht tun, wenn ich bedenke, dass meine Datenprovider-Basisklasse eine Liste und nicht die Liste verwendet, so dass ich keine Möglichkeit habe, beide Listen zu vergleichen. – GenericTypeTea

4

Wenn Sie bereit sind, Reflexion zu verwenden, die List<T> Klasse verfügt über ein eigenes Feld _version genannt, die jedes Mal die Liste Änderungen erhöht. Es wird Ihnen nicht sagen, welche Elemente geändert wurden, aber Sie können es mit dem ursprünglichen Wert _version vergleichen, um eine unmodifizierte Liste zu erkennen.

Als Referenz wird dieses Feld verwendet, um sicherzustellen, dass Enumeratoren ungültig werden, wenn die Liste geändert wird. Sie sollten es also ziemlich zuverlässig für Ihre Zwecke verwenden können, es sei denn, der tatsächlich verwaltete Code für List<T> ändert sich.

Um den Wert von _version erhalten Sie so etwas wie diese verwenden:

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

Allgemein gesprochen, obwohl dies nicht der beste Ansatz ist. Wenn Sie mit einem List<T> stecken, den jemand anderes erstellt hat, ist es wahrscheinlich die beste Option, die Sie haben. Beachten Sie, dass Änderungen am .NET-Framework den Namen des Felds ändern (oder vollständig entfernen) können. Es ist nicht garantiert, dass es in CLR-Implementierungen von Drittanbietern wie Mono vorhanden ist.

+0

Wie funktioniert das in Version 2.0 von CF? Das wäre perfekt. – GenericTypeTea

+0

Hat funktioniert, aber es wird nicht erhöht, wenn ein Element in der Liste geändert wird. – GenericTypeTea

1

Stellen Sie sicher, dass T ein Nachkomme eines Objekts mit einem schmutzigen Flag ist und dass die IList-Implementierung eine Überprüfung auf das aufweist, was die schmutzigen Flags der Liste durchläuft.

2

Wie wäre es mit so etwas?

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(); 
    } 
} 
2

Unter der Annahme, dass GetHashCode() für jedes Mitglied in der Liste enthalten sind ordnungsgemäß umgesetzt wird (und damit ändert sich, wenn ein Element ändert) ich etwas entlang der Linien von sich vorstellen:

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; 
     } 
    } 
} 

* Bitte Seien Sie sich bewusst, dass ich dies auf SO getippt habe und keinen Compiler habe, so dass der oben genannte Code in keiner Weise garantiert funktioniert, aber hoffentlich wird er die Idee zeigen.

Verwandte Themen