Feedback

Enumerator für 2D Arrays

Sprache: C#

Listen kann man mit Hilfe der [b]foreach[/b]-Schleife einfach durchlaufen. Doch wie ist es bei einem 2D Array ([b]int[,][/b])? Das funktioniert auch, jedoch kann man nicht ohne selbst mit zu zählen sagen, wo ein Item im Array liegt, was eigentlich gerade wichtig ist. Dieses Snippet löst das Problem. [b]Benötigte Namespaces[/b] System System.Collections System.Collections.Generic [b]Beispielanwendung[/b] [code]int[,] arr = { { 1, 3 }, { 2, 4 } }; //per foreach foreach (var item in arr.ToTwoDimensionalArrayEnumeratorContainer()) { Console.WriteLine("Item {0} at x={1}, y={2}", item.Item, item.X, item.Y); } //per selbst geschriebenem Enumerator using (var enu = arr.ToTwoDimensionalArrayEnumerator()) { while (enu.MoveNext()) { Console.WriteLine("Item {0} at x={1}, y={2}", enu.Current.Item, enu.Current.X, enu.Current.Y); } }[/code] [b]Hinweis[/b] Für die meisten Fälle würde auch eine verschachtelte [b]for[/b]-Schleife reichen, jedoch nicht wenn man beispielsweise an das Array binden möchte und die Position von jedem Element verarbeiten muss.
/// <summary>
/// Stellt Erweiterungsmethoden bereit.
/// </summary>
public static class Extensions
{
    /// <summary>
    /// Ruft einen speziellen Enumerator für ein zweidimensionales Array ab.
    /// </summary>
    /// <typeparam name="T">Der Typ der Elemente im Array.</typeparam>
    /// <param name="source">Das Quellarray, dessen Enumerator abgerufen werden soll.</param>
    /// <returns>Der Enumerator des Quellarrays.</returns>
    public static IEnumerator<TwoDimensionalArrayEnumeratorItem<T>> ToTwoDimensionalArrayEnumerator<T>(this T[,] source)
    {
        return new TwoDimensionalArrayEnumerator<T>(source);
    }

    /// <summary>
    /// Ruft einen Container für ein zweidimensionales Array ab, welcher einen speziellen Enumerator generieren kann.
    /// </summary>
    /// <typeparam name="T">Der Typ der Elemente im Array.</typeparam>
    /// <param name="source">Das Quellarray, das in einen Container gepackt werden soll.</param>
    /// <returns>Ein Container, welcher das Quellarray enthält.</returns>
    public static TwoDimensionalEnumeratorArrayContainer<T> ToTwoDimensionalArrayEnumeratorContainer<T>(this T[,] source)
    {
        return new TwoDimensionalEnumeratorArrayContainer<T>(source);
    }
}

/// <summary>
/// Stellt ein Element eines zweidimensionalen Arrays dar.
/// </summary>
/// <typeparam name="T">Der Typ des Elements.</typeparam>
public struct TwoDimensionalArrayEnumeratorItem<T>
{
    /// <summary>
    /// Initialisiert eine neue Instanz der <see cref="TwoDimensionalArrayEnumeratorItem{T}"/>-Klasse.
    /// </summary>
    /// <param name="x">Der Index der Spalte des Elements im Array.</param>
    /// <param name="y">Die Index der Zeile des Elements im Array.</param>
    /// <param name="item">Das Element des Arrays.</param>
    internal TwoDimensionalArrayEnumeratorItem(int x, int y, T item)
        : this()
    {
        this.X = x;
        this.Y = y;
        this.Item = item;
    }

    /// <summary>
    /// Ruft den Index der Spalte des Elements im Array ab.
    /// </summary>
    public int X { get; private set; }

    /// <summary>
    /// Ruft den Index der Zeile des Elements im Array ab.
    /// </summary>
    public int Y { get; private set; }

    /// <summary>
    /// Ruft das Element ab.
    /// </summary>
    public T Item { get; private set; }

    #region override

    /// <summary>
    /// Gibt eine Zeichenkette zurück, die das aktuelle Objekt darstellt. Nur für Debugzwecke.
    /// </summary>
    /// <returns>Eine Zeichenkette, die das aktuelle Objekt darstellt.</returns>
    public override string ToString()
    {
        return string.Format("TwoDimensionalEnumeratorItem at x = {0} and y = {1}, Item = {2}", this.X, this.Y, this.Item);
    }

    /// <summary>
    /// Ermittelt den Hashcode aus den Koordinaten des Elements und dem Element selbst.
    /// </summary>
    /// <returns>Der generierte Hashcode.</returns>
    public override int GetHashCode()
    {
        return this.X ^ this.Y ^ this.Item.GetHashCode();
    }

    /// <summary>
    /// Vergleicht ein Objekt mit dieser Instanz.
    /// </summary>
    /// <param name="obj">Das zu vergleichende Objekt.</param>
    /// <returns><c>true</c>, wenn <paramref name="obj"/> mit dieser Instanz übereinstimmt; andernfalls <c>false</c>.</returns>
    public override bool Equals(object obj)
    {
        if (!(obj is TwoDimensionalArrayEnumeratorItem<T>))
            return false;
        var item = (TwoDimensionalArrayEnumeratorItem<T>)obj;
        return item.X == this.X && item.Y == this.Y && this.Item.Equals(this.Item);
    }

    #endregion
}

/// <summary>
/// Stellt eine Containerklasse, für ein Zweidimensionales Array, dar, welche einen speziellen Enumerator für das Array abrufen kann.
/// </summary>
/// <typeparam name="TItem">Der Typ der Elemente im Array.</typeparam>
public sealed class TwoDimensionalEnumeratorArrayContainer<TItem> : IEnumerable<TwoDimensionalArrayEnumeratorItem<TItem>>
{
    TItem[,] source;
    /// <summary>
    /// Initialisiert eine neue Instanz der <see cref="TwoDimensionalEnumeratorArrayContainer{TItem}"/>-Klasse.
    /// </summary>
    /// <param name="source">Das Array, welches in den Container geladen werden soll.</param>
    public TwoDimensionalEnumeratorArrayContainer(TItem[,] source)
    {
        this.source = source;
    }

    /// <summary>
    /// Ruft den Enumerator für das Array ab.
    /// </summary>
    /// <returns></returns>
    public IEnumerator<TwoDimensionalArrayEnumeratorItem<TItem>> GetEnumerator()
    {
        return this.source.ToTwoDimensionalArrayEnumerator();
    }

    #region IEnumerable<SpecialEnumeratorItem<T>> Member

    IEnumerator<TwoDimensionalArrayEnumeratorItem<TItem>> IEnumerable<TwoDimensionalArrayEnumeratorItem<TItem>>.GetEnumerator()
    {
        return this.GetEnumerator();
    }

    #endregion

    #region IEnumerable Member

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

    #endregion

    #region override

    /// <summary>
    /// Gibt eine Zeichenkette zurück, die das aktuelle Objekt darstellt. Nur für Debugzwecke.
    /// </summary>
    /// <returns>Eine Zeichenkette, die das aktuelle Objekt darstellt.</returns>
    public override string ToString()
    {
        return string.Format("TwoDimensionalEnumeratorContainer: {0}", source.ToString());
    }

    /// <summary>
    /// Ruft den Hashcode des Quellarray ab.
    /// </summary>
    /// <returns>Der Hashcode des Quellarrays.</returns>
    public override int GetHashCode()
    {
        return source.GetHashCode();
    }

    /// <summary>
    /// Vergleicht ein Objekt mit dem Quellarray.
    /// </summary>
    /// <param name="obj">Das Objekt zum vergleichen.</param>
    /// <returns><c>true</c>, wenn <paramref name="obj"/> mit dem Quellarray übereinstimmt; andernfalls <c>false</c></returns>
    public override bool Equals(object obj)
    {
        return source.Equals(obj);
    }

    #endregion
}

/// <summary>
/// Stellt einen speziellen Enumerator für Zweidimensionale Arrays bereit.
/// </summary>
/// <typeparam name="TItem">Der Typ der Elemente, die der Enumerator zurück gibt.</typeparam>
public sealed class TwoDimensionalArrayEnumerator<TItem> : IEnumerator<TwoDimensionalArrayEnumeratorItem<TItem>>
{
    TItem[,] source;
    int mx, my;
    int x = -1, y = 0;
    TwoDimensionalArrayEnumeratorItem<TItem> current;

    /// <summary>
    /// Initialisiert eine neue Instanz der <see cref="TwoDimensionalArrayEnumerator{TItem}"/>-Klasse.
    /// </summary>
    /// <param name="source"></param>
    public TwoDimensionalArrayEnumerator(TItem[,] source)
    {
        this.source = source;
        this.mx = source.GetLength(0);
        this.my = source.GetLength(1);
    }

    #region IEnumerator<SpecialEnumeratorItem<T>> Member

    /// <summary>
    /// Ruft das aktuell vom Enumerator ausgewählte Element ab.
    /// </summary>
    public TwoDimensionalArrayEnumeratorItem<TItem> Current
    {
        get
        {
            return this.current;
        }
    }

    #endregion

    #region IDisposable Member

    void IDisposable.Dispose() { }

    #endregion

    #region IEnumerator Member

    object IEnumerator.Current
    {
        get
        {
            return this.current;
        }
    }

    /// <summary>
    /// Setzt den Enumerator auf das nächste Element im Array.
    /// </summary>
    /// <returns><c>true</c>, wenn der Enumerator auf das nächste Element gesetzt werden konnte; andernfalls <c>false</c></returns>
    public bool MoveNext()
    {
        if ((++this.x) >= mx)
        {
            if (++this.y >= my)
                return false;
            this.x = 0;
        }

        this.current = new TwoDimensionalArrayEnumeratorItem<TItem>(x, y, this.source[x, y]);
        return true;
    }

    /// <summary>
    /// Setzt den Enumerator auf seine Standardinitialisierung zurück.
    /// </summary>
    public void Reset()
    {
        this.x = -1;
        this.y = 0;
    }

    #endregion

    #region override

    /// <summary>
    /// Gibt eine Zeichenkette zurück, die das aktuelle Objekt darstellt. Nur für Debugzwecke.
    /// </summary>
    /// <returns>Eine Zeichenkette, die das aktuelle Objekt darstellt.</returns>
    public override string ToString()
    {
        return string.Format("TwoDimensionalEnumerator array with {1} columns and {0} rows.", this.x, this.y);
    }

    #endregion
}
/// <summary>
/// Stellt Erweiterungsmethoden bereit.
/// </summary>
public static class Extensions
{
    /// <summary>
    /// Ruft einen speziellen Enumerator für ein zweidimensionales Array ab.
    /// </summary>
    /// <typeparam name="T">Der Typ der Elemente im Array.</typeparam>
    /// <param name="source">Das Quellarray, dessen Enumerator abgerufen werden soll.</param>
    /// <returns>Der Enumerator des Quellarrays.</returns>
    public static IEnumerator<TwoDimensionalArrayEnumeratorItem<T>> ToTwoDimensionalArrayEnumerator<T>(this T[,] source)
    {
        return new TwoDimensionalArrayEnumerator<T>(source);
    }

    /// <summary>
    /// Ruft einen Container für ein zweidimensionales Array ab, welcher einen speziellen Enumerator generieren kann.
    /// </summary>
    /// <typeparam name="T">Der Typ der Elemente im Array.</typeparam>
    /// <param name="source">Das Quellarray, das in einen Container gepackt werden soll.</param>
    /// <returns>Ein Container, welcher das Quellarray enthält.</returns>
    public static TwoDimensionalEnumeratorArrayContainer<T> ToTwoDimensionalArrayEnumeratorContainer<T>(this T[,] source)
    {
        return new TwoDimensionalEnumeratorArrayContainer<T>(source);
    }
}

/// <summary>
/// Stellt ein Element eines zweidimensionalen Arrays dar.
/// </summary>
/// <typeparam name="T">Der Typ des Elements.</typeparam>
public struct TwoDimensionalArrayEnumeratorItem<T>
{
    /// <summary>
    /// Initialisiert eine neue Instanz der <see cref="TwoDimensionalArrayEnumeratorItem{T}"/>-Klasse.
    /// </summary>
    /// <param name="x">Der Index der Spalte des Elements im Array.</param>
    /// <param name="y">Die Index der Zeile des Elements im Array.</param>
    /// <param name="item">Das Element des Arrays.</param>
    internal TwoDimensionalArrayEnumeratorItem(int x, int y, T item)
        : this()
    {
        this.X = x;
        this.Y = y;
        this.Item = item;
    }

    /// <summary>
    /// Ruft den Index der Spalte des Elements im Array ab.
    /// </summary>
    public int X { get; private set; }

    /// <summary>
    /// Ruft den Index der Zeile des Elements im Array ab.
    /// </summary>
    public int Y { get; private set; }

    /// <summary>
    /// Ruft das Element ab.
    /// </summary>
    public T Item { get; private set; }

    #region override

    /// <summary>
    /// Gibt eine Zeichenkette zurück, die das aktuelle Objekt darstellt. Nur für Debugzwecke.
    /// </summary>
    /// <returns>Eine Zeichenkette, die das aktuelle Objekt darstellt.</returns>
    public override string ToString()
    {
        return string.Format("TwoDimensionalEnumeratorItem at x = {0} and y = {1}, Item = {2}", this.X, this.Y, this.Item);
    }

    /// <summary>
    /// Ermittelt den Hashcode aus den Koordinaten des Elements und dem Element selbst.
    /// </summary>
    /// <returns>Der generierte Hashcode.</returns>
    public override int GetHashCode()
    {
        return this.X ^ this.Y ^ this.Item.GetHashCode();
    }

    /// <summary>
    /// Vergleicht ein Objekt mit dieser Instanz.
    /// </summary>
    /// <param name="obj">Das zu vergleichende Objekt.</param>
    /// <returns><c>true</c>, wenn <paramref name="obj"/> mit dieser Instanz übereinstimmt; andernfalls <c>false</c>.</returns>
    public override bool Equals(object obj)
    {
        if (!(obj is TwoDimensionalArrayEnumeratorItem<T>))
            return false;
        var item = (TwoDimensionalArrayEnumeratorItem<T>)obj;
        return item.X == this.X && item.Y == this.Y && this.Item.Equals(this.Item);
    }

    #endregion
}

/// <summary>
/// Stellt eine Containerklasse, für ein Zweidimensionales Array, dar, welche einen speziellen Enumerator für das Array abrufen kann.
/// </summary>
/// <typeparam name="TItem">Der Typ der Elemente im Array.</typeparam>
public sealed class TwoDimensionalEnumeratorArrayContainer<TItem> : IEnumerable<TwoDimensionalArrayEnumeratorItem<TItem>>
{
    TItem[,] source;
    /// <summary>
    /// Initialisiert eine neue Instanz der <see cref="TwoDimensionalEnumeratorArrayContainer{TItem}"/>-Klasse.
    /// </summary>
    /// <param name="source">Das Array, welches in den Container geladen werden soll.</param>
    public TwoDimensionalEnumeratorArrayContainer(TItem[,] source)
    {
        this.source = source;
    }

    /// <summary>
    /// Ruft den Enumerator für das Array ab.
    /// </summary>
    /// <returns></returns>
    public IEnumerator<TwoDimensionalArrayEnumeratorItem<TItem>> GetEnumerator()
    {
        return this.source.ToTwoDimensionalArrayEnumerator();
    }

    #region IEnumerable<SpecialEnumeratorItem<T>> Member

    IEnumerator<TwoDimensionalArrayEnumeratorItem<TItem>> IEnumerable<TwoDimensionalArrayEnumeratorItem<TItem>>.GetEnumerator()
    {
        return this.GetEnumerator();
    }

    #endregion

    #region IEnumerable Member

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

    #endregion

    #region override

    /// <summary>
    /// Gibt eine Zeichenkette zurück, die das aktuelle Objekt darstellt. Nur für Debugzwecke.
    /// </summary>
    /// <returns>Eine Zeichenkette, die das aktuelle Objekt darstellt.</returns>
    public override string ToString()
    {
        return string.Format("TwoDimensionalEnumeratorContainer: {0}", source.ToString());
    }

    /// <summary>
    /// Ruft den Hashcode des Quellarray ab.
    /// </summary>
    /// <returns>Der Hashcode des Quellarrays.</returns>
    public override int GetHashCode()
    {
        return source.GetHashCode();
    }

    /// <summary>
    /// Vergleicht ein Objekt mit dem Quellarray.
    /// </summary>
    /// <param name="obj">Das Objekt zum vergleichen.</param>
    /// <returns><c>true</c>, wenn <paramref name="obj"/> mit dem Quellarray übereinstimmt; andernfalls <c>false</c></returns>
    public override bool Equals(object obj)
    {
        return source.Equals(obj);
    }

    #endregion
}

/// <summary>
/// Stellt einen speziellen Enumerator für Zweidimensionale Arrays bereit.
/// </summary>
/// <typeparam name="TItem">Der Typ der Elemente, die der Enumerator zurück gibt.</typeparam>
public sealed class TwoDimensionalArrayEnumerator<TItem> : IEnumerator<TwoDimensionalArrayEnumeratorItem<TItem>>
{
    TItem[,] source;
    int mx, my;
    int x = -1, y = 0;
    TwoDimensionalArrayEnumeratorItem<TItem> current;

    /// <summary>
    /// Initialisiert eine neue Instanz der <see cref="TwoDimensionalArrayEnumerator{TItem}"/>-Klasse.
    /// </summary>
    /// <param name="source"></param>
    public TwoDimensionalArrayEnumerator(TItem[,] source)
    {
        this.source = source;
        this.mx = source.GetLength(0);
        this.my = source.GetLength(1);
    }

    #region IEnumerator<SpecialEnumeratorItem<T>> Member

    /// <summary>
    /// Ruft das aktuell vom Enumerator ausgewählte Element ab.
    /// </summary>
    public TwoDimensionalArrayEnumeratorItem<TItem> Current
    {
        get
        {
            return this.current;
        }
    }

    #endregion

    #region IDisposable Member

    void IDisposable.Dispose() { }

    #endregion

    #region IEnumerator Member

    object IEnumerator.Current
    {
        get
        {
            return this.current;
        }
    }

    /// <summary>
    /// Setzt den Enumerator auf das nächste Element im Array.
    /// </summary>
    /// <returns><c>true</c>, wenn der Enumerator auf das nächste Element gesetzt werden konnte; andernfalls <c>false</c></returns>
    public bool MoveNext()
    {
        if ((++this.x) >= mx)
        {
            if (++this.y >= my)
                return false;
            this.x = 0;
        }

        this.current = new TwoDimensionalArrayEnumeratorItem<TItem>(x, y, this.source[x, y]);
        return true;
    }

    /// <summary>
    /// Setzt den Enumerator auf seine Standardinitialisierung zurück.
    /// </summary>
    public void Reset()
    {
        this.x = -1;
        this.y = 0;
    }

    #endregion

    #region override

    /// <summary>
    /// Gibt eine Zeichenkette zurück, die das aktuelle Objekt darstellt. Nur für Debugzwecke.
    /// </summary>
    /// <returns>Eine Zeichenkette, die das aktuelle Objekt darstellt.</returns>
    public override string ToString()
    {
        return string.Format("TwoDimensionalEnumerator array with {1} columns and {0} rows.", this.x, this.y);
    }

    #endregion
}