Feedback

C# - Enumerator für 2D Arrays

Veröffentlicht von am 8/17/2014
(0 Bewertungen)
Listen kann man mit Hilfe der foreach-Schleife einfach durchlaufen. Doch wie ist es bei einem 2D Array (int[,])? 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.

Benötigte Namespaces
System
System.Collections
System.Collections.Generic

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


Hinweis
Für die meisten Fälle würde auch eine verschachtelte for-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
}

Kommentare zum Snippet

 

Logge dich ein, um hier zu kommentieren!