Laden...

IndexedItemList <T>

Erstellt von herbivore vor 14 Jahren Letzter Beitrag vor 14 Jahren 2.983 Views
herbivore Themenstarter:in
49.485 Beiträge seit 2005
vor 14 Jahren
IndexedItemList <T>

Beschreibung:

Worum geht es?

Ich habe eine Klasse IndexedItemList <T> geschrieben, die ich euch zur Verfügung stellen möchte.

Die Einträge der IndexedItemList müssen das Interface IIndexedItem implementieren und haben dadurch eine Index-Property. Die IndexedItemList sorgt dafür, dass der Wert der Index-Property immer (d.h. bei allen Änderungen an der Liste oder an der Index-Property) mit dem Index des Eintrags in der Liste übereinstimmt.

Dadurch ist automatisch dafür gesorgt, dass ein Eintrag immer nur genau in einer Liste enthalten sein kann. Fügt man es einer anderen Liste hinzu, wird es automatisch aus der ursprünglichen Liste entfernt. Das klappt lustigerweise, obwohl das Eintrag nicht weiß, in welcher Liste er enthalten ist.

Man kann die Index-Property auch ändern. Dadurch wird automatisch die Position des Eintrags in der Liste angepasst.

Wozu das Ganze?

Zum einen ist die IndexedItemList praktisch, weil man auf die Art in einer foreach-Schleife Zugriff auf den Index des aktuellen Elements hat und nicht die foreach-Schleife nur wegen des Zugriffs auf den Index auf eine for-Schleife umstellen muss und zum anderen finde ich es in manchen Fällen ganz praktisch, dass sichergestellt ist, dass ein Element immer in genau (bzw. maximal) einer Liste enthalten ist.

Im Gegensatz zu IndexOf, welches eine O(n)-Operation ist, ist das Abrufen von IIndexedItem.Index eine O(1)-Operation.

Woraus besteht die Komponente?

Der folgende Code enthält neben der eigentlichen IndexedItemList <T>-Klasse,

  • das Interface IIndexedItem für die Items,
  • die Klasse IndexedItemBase, von der die eigene Item-Klasse erben kann, um das Interface automatisch korrekt zu implementieren und
  • eine passende EventArgs-Klasse als Parameter für das IndexChanged-Event sowie
  • eine kleine Beispiel-Item-Klasse und
  • ein kleines Demo-Programm.

Der Code ist getestet und sollte korrekt sein (falls ihr trotzdem einen Fehler findet, sagt mir bitte Bescheid). Außerdem gibt es eine ausführliche Dokumentation im XML- und CHM-Format. Im Anhang findet ihr eine ZIP-Datei, die IndexedItemList.cs, IndexedItemListDemo.cs, IndexedItemList.chm und IndexedItemList.xml enthält.


using System;
using System.Collections.ObjectModel;
using System.Diagnostics;

//*****************************************************************************
public interface IIndexedItem
{
   //==========================================================================
   int Index { get; set; }

   //==========================================================================
   event EventHandler <IndexedItemEventArgs> IndexChanged;
}

//*****************************************************************************
public class IndexedItemEventArgs : EventArgs
{
   //--------------------------------------------------------------------------
   private int _iOldIndex;

   //==========================================================================
   public IndexedItemEventArgs (int iOldIndex)
   {
      _iOldIndex = iOldIndex;
   }

   //==========================================================================
   public int OldIndex
   {
      get { return _iOldIndex; }
   }
}

//*****************************************************************************
public class IndexedItemBase : IIndexedItem
{
   //--------------------------------------------------------------------------
   private int _iIndex = -1;

   //==========================================================================
   public int Index
   {
      get {
         return _iIndex;
      }

      set {
         //--------------------------------------------------------------------
         // Negative Werte auf -1 normieren
         //--------------------------------------------------------------------
         if (value < -1) { value = -1; }

         //--------------------------------------------------------------------
         // Wenn der Wert sich nicht ändert, ist nichts zu tun
         //--------------------------------------------------------------------
         if (_iIndex == value) { return; }

         //--------------------------------------------------------------------
         // Setzen des Wertes und Auslösen des IndexChanged-Events
         //--------------------------------------------------------------------
         IndexedItemEventArgs e = new IndexedItemEventArgs (_iIndex);
         _iIndex = value;
         OnIndexChanged (e);
      }
   }

   //==========================================================================
   public event EventHandler <IndexedItemEventArgs> IndexChanged;

   //==========================================================================
   protected virtual void OnIndexChanged (IndexedItemEventArgs e)
   {
      EventHandler <IndexedItemEventArgs> indexChanged = IndexChanged;
      if (indexChanged != null) {
         indexChanged (this, e);
      }
   }
}

//*****************************************************************************
public class IndexedItemList <T> : Collection <T> where T : IIndexedItem
{
   //--------------------------------------------------------------------------
   private bool _fAvoidRecursiveCall  = false;
   private bool _fItemInThisList      = false;

   //==========================================================================
   public void Move (int iOldIndex, int iNewIndex)
   {
      //-----------------------------------------------------------------------
      // Parameter prüfen
      //-----------------------------------------------------------------------
      if (iOldIndex < 0 || iOldIndex > Items.Count) {
         throw new ArgumentOutOfRangeException ("iOldIndex");
      }
      if (iNewIndex < 0 || iNewIndex > Items.Count) {
         throw new ArgumentOutOfRangeException ("iNewIndex");
      }

      //-----------------------------------------------------------------------
      // Wenn Start- und Zielposition gleich sind, ist nichts weiter zu tun
      //-----------------------------------------------------------------------
      if (iNewIndex == iOldIndex) { return; }

      //-----------------------------------------------------------------------
      // Eigentliche Aktion durchführen
      //-----------------------------------------------------------------------
      MoveItem (iOldIndex, iNewIndex);
   }

   //==========================================================================
   public virtual void MoveItem (int iOldIndex, int iNewIndex)
   {
      //-----------------------------------------------------------------------
      // Rekursiven Aufruf verhindern
      //-----------------------------------------------------------------------
      try {
         _fAvoidRecursiveCall = true;

         //--------------------------------------------------------------------
         // Eigentliche Aktion durchführen
         //--------------------------------------------------------------------
         MoveItemCore (iOldIndex, iNewIndex);
      }
      finally {
         _fAvoidRecursiveCall = false;
      }
   }

   //==========================================================================
   protected virtual void MoveItemCore (int iOldIndex, int iNewIndex)
   {
      //-----------------------------------------------------------------------
      // Merken des zu verschiebenden Eintrags
      //-----------------------------------------------------------------------
      T tItem = Items [iOldIndex];

      //-----------------------------------------------------------------------
      // Ermitteln, in welche Richtung verschoben werden muss
      //-----------------------------------------------------------------------
      if (iOldIndex > iNewIndex) {
         //--------------------------------------------------------------------
         // Bestehende Einträge zwischen (ausschließlich) Start- und
         // (einschließlich) Zielposition nach rechts verschieben
         //--------------------------------------------------------------------
         for (int i = iOldIndex; i > iNewIndex; --i) {
            Items [i] = Items [i-1];
            ++Items [i].Index;
         }
      } else {
         //--------------------------------------------------------------------
         // Bestehende Einträge zwischen (ausschließlich) Start- und
         // (einschließlich) Zielposition nach links verschieben
         //--------------------------------------------------------------------
         for (int i = iOldIndex; i < iNewIndex; ++i) {
            Items [i] = Items [i+1];
            --Items [i].Index;
         }
      }

      //-----------------------------------------------------------------------
      // Zu verschiebenden Eintrag an Zielposition setzen
      //-----------------------------------------------------------------------
      Items [iNewIndex] = tItem;
      tItem.Index = iNewIndex;
   }

   //==========================================================================
   public void Swap (int iIndex1, int iIndex2)
   {
      //-----------------------------------------------------------------------
      // Parameter prüfen
      //-----------------------------------------------------------------------
      if (iIndex1 < 0 || iIndex1 > Items.Count) {
         throw new ArgumentOutOfRangeException ("iIndex1");
      }
      if (iIndex2 < 0 || iIndex2 > Items.Count) {
         throw new ArgumentOutOfRangeException ("iIndex2");
      }

      //-----------------------------------------------------------------------
      // Wenn beide Positionen gleich sind, ist nichts weiter zu tun
      //-----------------------------------------------------------------------
      if (iIndex1 == iIndex2) { return; }

      //-----------------------------------------------------------------------
      // Eigentliche Aktion durchführen
      //-----------------------------------------------------------------------
      SwapItems (iIndex1, iIndex2);
   }

   //==========================================================================
   public virtual void SwapItems (int iIndex1, int iIndex2)
   {
      //-----------------------------------------------------------------------
      // Rekursiven Aufruf verhindern
      //-----------------------------------------------------------------------
      try {
         _fAvoidRecursiveCall = true;

         //--------------------------------------------------------------------
         // Eigentliche Aktion durchführen
         //--------------------------------------------------------------------
         SwapItemsCore (iIndex1, iIndex2);
      }
      finally {
         _fAvoidRecursiveCall = false;
      }
   }

   //==========================================================================
   protected virtual void SwapItemsCore (int iIndex1, int iIndex2)
   {
      //-----------------------------------------------------------------------
      // Eigentliche Aktion auf der Liste durchführen
      //-----------------------------------------------------------------------
      T tItem = Items [iIndex1];
      Items [iIndex1] = Items [iIndex2];
      Items [iIndex2] = tItem;

      //-----------------------------------------------------------------------
      // Beide Einträge aktualisieren
      //-----------------------------------------------------------------------
      Items [iIndex1].Index = iIndex1;
      Items [iIndex2].Index = iIndex2;
   }


   //==========================================================================
   protected override void InsertItem (int iIndex, T tItem)
   {
      //-----------------------------------------------------------------------
      // Verhindern, dass Null-Referenzen in die Liste kommen
      //-----------------------------------------------------------------------
      if (tItem == null) {
         throw new ArgumentNullException ("tItem");
      }

      //-----------------------------------------------------------------------
      // Rekursiven Aufruf verhindern
      //-----------------------------------------------------------------------
      try {
         _fAvoidRecursiveCall = true;

         //--------------------------------------------------------------------
         // Falls der neue Eintrag in einer anderen Liste enthalten ist,
         // löst der Setter von Index aus, dass der den Eintrag dort
         // rausfliegt.
         // Falls der neue Eintrag in unserer Liste ist, löst der der Setter
         // von Index aus, dass unser _fItemInThisList auf true gesetzt wird.
         //--------------------------------------------------------------------
         int iOldIndex = tItem.Index;
         _fItemInThisList = false;
         tItem.Index = -1;

         //--------------------------------------------------------------------
         // Wenn der Eintrag schon in der aktuellen Liste ist, müssen wir ihn
         // an die richtige Position verschieben
         //--------------------------------------------------------------------
         if (_fItemInThisList) {
            MoveItemCore (iOldIndex, Math.Min (iIndex, Items.Count - 1));
            return;
         }

         //--------------------------------------------------------------------
         // Ansonsten müssen wir ihn einfügen
         //--------------------------------------------------------------------
         InsertItemCore (iIndex, tItem);

      }
      finally {
         _fAvoidRecursiveCall = false;
      }
   }

   //==========================================================================
   protected virtual void InsertItemCore (int iIndex, T tItem)
   {
      //-----------------------------------------------------------------------
      // Eigentliche Aktion auf der Liste durchführen
      //-----------------------------------------------------------------------
      base.InsertItem (iIndex, tItem);

      //-----------------------------------------------------------------------
      // Neuen Eintrag aktualisieren
      //-----------------------------------------------------------------------
      tItem.Index         = iIndex;
      tItem.IndexChanged += Item_IndexChanged;

      //-----------------------------------------------------------------------
      // Alle Einträge nach dem neuen Eintrag aktualisieren
      //-----------------------------------------------------------------------
      for (int i = iIndex + 1; i < Items.Count; ++i) {
         Items [i].Index = i;
      }

   }

   //==========================================================================
   protected override void SetItem (int iIndex, T tItem)
   {
      //-----------------------------------------------------------------------
      // Verhindern, dass Null-Referenzen in die Liste kommen
      //-----------------------------------------------------------------------
      if (tItem == null) {
         throw new ArgumentNullException ("tItem");
      }

      //-----------------------------------------------------------------------
      // Wenn der neue Eintrag schon der aktuelle ist, ist nichts zu tun
      //-----------------------------------------------------------------------
      if (Object.ReferenceEquals (Items [iIndex], tItem)) { return; }

      //-----------------------------------------------------------------------
      // Rekursiven Aufruf verhindern
      //-----------------------------------------------------------------------
      try {
         _fAvoidRecursiveCall = true;

         //--------------------------------------------------------------------
         // Falls der neue Eintrag in einer anderen Liste enthalten ist,
         // löst der Setter von Index aus, dass der den Eintrag dort
         // rausfliegt.
         // Falls der neue Eintrag in unserer Liste ist, löst der der Setter
         // von Index aus, dass unser _fItemInThisList auf true gesetzt wird.
         //--------------------------------------------------------------------
         int iOldIndex = tItem.Index;
         _fItemInThisList = false;
         tItem.Index = -1;

         //--------------------------------------------------------------------
         // Wenn der neue Eintrag schon in der aktuellen Liste ist,
         // kommt es darauf an, ob er rechts oder links von dem
         // zu überschreibenden Eintrag steht.
         //--------------------------------------------------------------------
         if (_fItemInThisList) {
            if (iOldIndex < iIndex) {
               //--------------------------------------------------------------
               // Wenn der neue Eintrag links von dem zu überschreibenden
               // steht, verschieben wir den neuen Eintrag vor den zu
               // überschreibenden und entfernen den zu überschreibenden.
               //--------------------------------------------------------------
               MoveItemCore (iOldIndex, iIndex - 1);
               RemoveItemCore (iIndex);
               return;
            }
            //-----------------------------------------------------------------
            // Wenn der neue Eintrag rechts von dem zu überschreiben steht
            // (an identischer Position können sie aufgrund einer früheren
            // Abfrage nicht stehen), vertauschen wir beiden Einträge
            // und entfernen dann den zu überschreibenden an seinem neuen Ort.
            //-----------------------------------------------------------------
            SwapItemsCore (iOldIndex, iIndex);
            RemoveItemCore (iOldIndex);
            return;
         }

         //--------------------------------------------------------------------
         // Ansonsten müssen mit dem neuen Eintrag den bestehenden Eintrag
         // überschreiben
         //--------------------------------------------------------------------
         SetItemCore (iIndex, tItem);
      }
      finally {
         _fAvoidRecursiveCall = false;
      }
   }

   //==========================================================================
   protected virtual void SetItemCore (int iIndex, T tItem)
   {
      //-----------------------------------------------------------------------
      // Bestehenden Eintrag aktualisieren
      //-----------------------------------------------------------------------
      Items [iIndex].Index         = -1;
      Items [iIndex].IndexChanged -= Item_IndexChanged;

      //-----------------------------------------------------------------------
      // Neuen Eintrag aktualisieren
      //-----------------------------------------------------------------------
      tItem.Index         = iIndex;
      tItem.IndexChanged += Item_IndexChanged;

      //-----------------------------------------------------------------------
      // Eigentliche Aktion auf der Liste durchführen
      //-----------------------------------------------------------------------
      base.SetItem (iIndex, tItem);
   }

   //==========================================================================
   protected override void RemoveItem (int iIndex)
   {
      //-----------------------------------------------------------------------
      // Rekursiven Aufruf verhindern
      //-----------------------------------------------------------------------
      try {
         _fAvoidRecursiveCall = true;

         //--------------------------------------------------------------------
         // Eigentliche Aktion durchführen
         //--------------------------------------------------------------------
         RemoveItemCore (iIndex);
      }
      finally {
         _fAvoidRecursiveCall = false;
      }
   }

   //==========================================================================
   protected virtual void RemoveItemCore (int iIndex)
   {
      //-----------------------------------------------------------------------
      // Bestehenden Eintrag aktualisieren
      //-----------------------------------------------------------------------
      Items [iIndex].Index         = -1;
      Items [iIndex].IndexChanged -= Item_IndexChanged;

      //-----------------------------------------------------------------------
      // Eigentliche Aktion auf der Liste durchführen
      //-----------------------------------------------------------------------
      base.RemoveItem (iIndex);

      //-----------------------------------------------------------------------
      // Alle Einträge nach dem bestehenden Eintrag aktualisieren
      //-----------------------------------------------------------------------
      for (int i = iIndex; i < Items.Count; ++i) {
         Items [i].Index = i;
      }
   }

   //==========================================================================
   protected override void ClearItems ()
   {
      try {
         _fAvoidRecursiveCall = true;

         //--------------------------------------------------------------------
         // Alle Einträge aktualisieren
         //--------------------------------------------------------------------
         foreach (T tItem in Items) {
            tItem.Index         = -1;
            tItem.IndexChanged -= Item_IndexChanged;
         }

         //--------------------------------------------------------------------
         // Eigentliche Aktion auf der Liste durchführen
         //--------------------------------------------------------------------
         base.ClearItems ();
      }
      finally {
         _fAvoidRecursiveCall = false;
      }
   }

   //==========================================================================
   // EventHander für das IIndexItem.IndexChanged-Ereignis.
   //
   // Durch diesen wird sichergestellt, dass die Änderung der
   // Index-Eigenschaft eines Eintrags, auch die Position des
   // Eintrags in der Liste, in der er sich befindet, ändert.
   //
   // Außerdem wird der EventHandler dazu verwendet, um zu ermitteln,
   // ob sich ein Eintrag schon in der aktuellen Liste befindet.
   private void Item_IndexChanged (Object objSender, IndexedItemEventArgs e)
   {
      //-----------------------------------------------------------------------
      // Wir setzen die Variable _fInThisList von der Liste, in der sich
      // der Eintrag befindet.
      //-----------------------------------------------------------------------
      _fItemInThisList = true;

      //-----------------------------------------------------------------------
      // Wenn die Events aufgrund interner Änderungen kommen, dürfen wir
      // nichts weiter tun.
      //-----------------------------------------------------------------------
      if (_fAvoidRecursiveCall) { return; }

      //-----------------------------------------------------------------------
      // Wir holen uns den aktuellen Eintrag und seinen aktuellen (=neuen)
      // Index
      //-----------------------------------------------------------------------
      T tItem = (T)objSender;
      int iNewIndex = tItem.Index;

      //-----------------------------------------------------------------------
      // Eigentlich sollte der Implementierer der Item-Klasse dafür sorgen,
      // dass der IndexChanged-Event nicht ausgelöst wird, wenn sich der
      // Wert nicht geändert hat, aber darauf verlassen wir uns nicht
      // und prüfen hier nochmal. Außerdem sollte der Implementierer
      // der Item-Klasse dafür sorgen, dass alle negativen Werte auf -1
      // normiert werden. Auch darauf verlassen wir uns nicht.
      // Wenn also eine Veränderung gemeldet wird, die keine ist, ignorieren
      // wir die Meldung.
      //-----------------------------------------------------------------------
      if (iNewIndex == e.OldIndex || (iNewIndex < 0 && e.OldIndex < 0)) {
         return;
      }

      //-----------------------------------------------------------------------
      // Ermitteln, welche Operation durch das Setzen des Index ausgelöst
      // werden soll. Wenn dabei was schiefgeht ...
      //-----------------------------------------------------------------------
      try {
         if (e.OldIndex >= 0) {
            if (iNewIndex >= 0) {
               //--------------------------------------------------------------
               // Move
               //--------------------------------------------------------------
               Move (e.OldIndex, iNewIndex);
            } else {
               //--------------------------------------------------------------
               // Remove
               //--------------------------------------------------------------
               RemoveAt (e.OldIndex);
            }
         } else {
            if (iNewIndex >= 0) {
               //--------------------------------------------------------------
               // Insert
               // Anm.: Dieser Fall kann nicht eintreten
               //--------------------------------------------------------------
               // dieser Fall kann nicht eintreten
               Debug.Assert (false);
            } else {
               //--------------------------------------------------------------
               // Nop (nicht enthaltener Eintrag soll weiterhin nicht
               //      enthalten sein)
               // Anm.: Dieser Fall kann nicht eintreten
               //--------------------------------------------------------------
               Debug.Assert (false);
            }
         }
      }
      catch (Exception) {
         //--------------------------------------------------------------------
         // ... setzen wir den Index wieder auf den alten Wert ...
         //--------------------------------------------------------------------
         try {
            _fAvoidRecursiveCall = true;
            tItem.Index = e.OldIndex;
         }
         finally {
            _fAvoidRecursiveCall = false;
         }

         //--------------------------------------------------------------------
         // ... werfen die Exception aber weiter.
         //--------------------------------------------------------------------
         throw;
      }
   }
}


using System;

//*****************************************************************************
/// <summary>
/// </summary>
public class MyItem : IndexedItemBase
{
   //--------------------------------------------------------------------------
   private String _strName;

   //==========================================================================
   /// <summary>
   /// </summary>
   public MyItem (String strName)
   {
      _strName = strName;
   }

   //==========================================================================
   /// <summary>
   /// </summary>
   public String Name
   {
      get { return _strName; }
      set { _strName = value; }
   }

   //==========================================================================
   /// <summary>
   /// </summary>
   public override String ToString ()
   {
      return _strName;
   }
}

//*****************************************************************************
/// <summary>
/// </summary>
static class App
{
   //==========================================================================
   /// <summary>
   /// </summary>
   public static void Main (string [] astrArg)
   {
      //-----------------------------------------------------------------------
      IndexedItemList <MyItem> list1 = new IndexedItemList <MyItem> ();
      for (int i = 0; i < 4; ++i) {
         list1.Add (new MyItem ("Item1_" + i));
      }

      //-----------------------------------------------------------------------
      list1 [2].Index = 0;

      //-----------------------------------------------------------------------
      Console.WriteLine ();
      foreach (MyItem item in list1) {
         Console.WriteLine ("" + item.Index + ": " + item.Name);
      }


      //-----------------------------------------------------------------------
      IndexedItemList <MyItem> list2 = new IndexedItemList <MyItem> ();
      for (int i = 0; i < 4; ++i) {
         list2.Add (new MyItem ("Item2_" + i));
      }

      IndexedItemList <MyItem> list3 = new IndexedItemList <MyItem> ();
      for (int i = 0; i < 4; ++i) {
         list3.Add (new MyItem ("Item3_" + i));
      }

      //-----------------------------------------------------------------------
      list2.Insert (0, list3 [1]);

      //-----------------------------------------------------------------------
      Console.WriteLine ();
      foreach (MyItem item in list2) {
         Console.WriteLine ("" + item.Index + ": " + item.Name);
      }

      Console.WriteLine ();
      foreach (MyItem item in list3) {
         Console.WriteLine ("" + item.Index + ": " + item.Name);
      }

   }
}

Das Demo-Programm erzeugt folgende Ausgabe:


0: Item1_2
1: Item1_0
2: Item1_1
3: Item1_3

0: Item3_1
1: Item2_0
2: Item2_1
3: Item2_2
4: Item2_3

0: Item3_0
1: Item3_2
2: Item3_3

Schlagwörter: Index, indexiert, indexierte, indiziert, indizierte, List, Liste, Element, Elemente, Indexzugriff

herbivore Themenstarter:in
49.485 Beiträge seit 2005
vor 14 Jahren

Hallo zusammen,

da ich aus anderem Anlass sowieso gerade die Klassenbeschreibung von IndexItemList<T> als BBCode formatiert habe, hänge ich sie gleich mal hier an:

[B]Zusammenfassung[/B]

Stellt eine stark typisierte Liste von Objekten (=Einträgen) dar, auf die über einen Index zugegriffen werden kann, wobei die Einträge ihren eigenen Index kennen.

[B]Beschreibung[/B]

Diese Klasse erbt von System.Collections.ObjectModel.Collection <T> und stellt daher mehr Methoden zur Verfügung als hier beschrieben, inbesondere die Methoden, die durch IList implementiert sind: Add, Clear, Contains, IndexOf, Insert, Remove, RemoveAt. Die Beschreibung der zusätzlichen Methoden finden Sie in der MSDN-Dokumentation. Durch das Überschreiben von Methoden in dieser Klasse ändert sich das jedoch Verhalten der genannten Methoden gebenüber der Beschreibung in der MSDN. Deshalb sollte zusätzlich die Beschreibung der überschrieben Methoden in dieser Dokumentation konsultiert werden.

Die Einträge der Liste müssen das Interface IIndexedItem implementieren.

Auf Einträge in dieser Auflistung kann mithilfe eines ganzzahligen Index zugegriffen werden. Diese Auflistung verwendet nullbasierte Indizes.

Die Liste ist änderbar. Einen schreibgeschützten Wrapper, den Sie für diese Liste verwenden können, finden Sie unter ReadOnlyCollection <T> in der MSDN-Doku.

Die Liste stellt sicher, dass bei jeder Änderung an der Liste IIndexedItem.Index jedes Eintrags mit dem Index des Eintrags in der Liste übereinstimmt (vorausgesetzt IIndexedItem.IndexChanged ist korrekt implementiert). Andersherum stellt die Liste sicher, dass jede zulässige Änderung an IIndexedItem.Index den Eintrag innerhalb der Liste an den entsprechenden Index verschiebt.

Die Liste lehnt null als Wert für Verweistypen ab und lässt keine doppelten Einträge zu, auch nicht listenübergreifend. Ein Eintrag befindet sich also zu jedem Zeitpunkt in gar keiner oder in genau einer IndexedItemList.

Wird ein Eintrag in eine Liste hinzugefügt (Add/Insert/Set), in der er sich schon befindet, wird er von seiner bisherigen Position an die neue Position verschoben. Erfolgt die Verschiebung dabei von von einem niedrigeren zu einem höheren Index, ist der Index des Eintrags nach der Operation um eins niedriger als angegeben. Nach list.Insert (8, list [5]) hat der Eintrag den Index 7.

Wird ein Eintrag in eine Liste hinzugefügt (Add/Insert/Set), obwohl er sich schon in einer anderen IndexedItemList befindet, wird er aus der anderen Liste entfernt.

In dieser Klasse gibt drei Arten von Methoden:
[LIST]
[*]Die öffentlichen Action-Methoden (Move, Swap, ...)
Diese prüfen die Parameter und rufen die zugehörigen ActionItem-Methoden auf.

[*]Die geschützten ActionItem(s)-Methoden (MoveItem, SwapItems, ...)
Diese können sich - bis auf die Prüfung von tItem auf null, die sie selbst durchführen - auf gültige Parameter und einen konsistenten Zustand verlassen. Sie verhindern einen möglichen rekursiven Aufruf durch den Setter von Index, ermitteln aufgrund des Zustandes, was genau zu tun ist und rufen dann die entsprechende ActionItem(s)Core-Methoden auf.

[*]Die geschützten ActionItem(s)Core-Methoden (MoveItemCore, SwapItemsCore, ...)
Diese können sich auf gültige Parameter verlassen und darauf, dass der rekursive Aufruf durch den Setter von Index verhindert ist. Der Zustand des aktuellen Eintrags ist möglicherweise schon inkonsistent. Sie führen die eigentliche Aktion aus und tun dabei genau, was sie gesagt bekommen. Anschließend bringen sie den aktuellen Eintrag wieder in einen gültigen Zustand.
[/LIST]

herbivore