C#でデザインパターン「Iteratorパターン」

C# コンピュータ
C#

C#ではList<T>などIEnumerable<T>インターフェースを実装したコレクションと、それをループ処理するforeachがあるので、Iteratorパターンを実装する機会は少ないと思われます。

サンプルコード

using System.Collections;
using System.Collections.Generic;
using System.Linq;

/// <summary>
/// Iterator パターンの Aggregate インターフェース
/// </summary>
interface INulltableCollection<T> : IEnumerable<T>
{
    IIterator<T> CreateIterator { get; }
}

/// <summary>
/// Iterator パターンの Iterator インターフェース
/// </summary>
interface IIterator<T> : IEnumerator<T>
{
    new bool MoveNext();
    new T Current { get; }
    new void Reset();
}

/// <summary>
/// Iterator パターンの ConcreteAggregate
/// </summary>
public class NulltableCollection<T> : INulltableCollection<T>
{
    private readonly List<T> _list = new List<T>(1); // 最大要素数を1に制限

    public NulltableCollection() { }
    public NulltableCollection(T? item)
    {
        if (item is not null)
        {
            _list.Add(item);
        }
    }

    IIterator<T> INulltableCollection<T>.CreateIterator => new NulltableCollectionIterator<T>(this);

    public IEnumerator<T> GetEnumerator() => ((INulltableCollection<T>)this).CreateIterator;

    IEnumerator IEnumerable.GetEnumerator() => ((INulltableCollection<T>)this).CreateIterator;

    // 内部リストへのアクセスを提供する (Iteratorからのみアクセスを想定)
    internal List<T> GetInternalList() => _list;
}

/// <summary>
/// Iterator パターンの ConcreteIterator
/// </summary>
public class NulltableCollectionIterator<T> : IIterator<T>
{
    private readonly NulltableCollection<T> _collection;
    private int _currentIndex = -1;

    public NulltableCollectionIterator(NulltableCollection<T> collection)
    {
        _collection = collection;
    }

    public T Current
    {
        get
        {
            if (_currentIndex < 0 || _currentIndex >= _collection.GetInternalList().Count)
            {
                throw new InvalidOperationException("イテレーターは有効な位置にありません。");
            }
            return _collection.GetInternalList()[_currentIndex];
        }
    }

    object IEnumerator.Current
    {
        get
        {
            if (_currentIndex < 0 || _currentIndex >= _collection.GetInternalList().Count)
            {
                throw new InvalidOperationException("イテレーターは有効な位置にありません。");
            }
            return Current!;
        }
    }

    public void Dispose() { /* 特にリソースがないため何もしない */ }

    public bool MoveNext()
    {
        _currentIndex++;
        return _currentIndex < _collection.GetInternalList().Count;
    }

    public void Reset()
    {
        _currentIndex = -1;
    }
}

static class Program
{
    static public void Main()
    {
        NulltableCollection<string> vars1 = new ("Hello");
        Console.WriteLine("vars1:");
        foreach (string v in vars1)
        {
            Console.WriteLine(v);
        }

        NulltableCollection<string> vars2 = new (null);
        Console.WriteLine("\nvars2:");
        foreach (string v in vars2)
        {
            // vars2 は要素を持ちません
            Console.WriteLine(v);
        }
    }
}

要素数が0か1つだけしか保持出来ないのでコレクションとしては使い道が無いですが、null許容型の変数のnullチェックをifではなくforeachで行おうという試みです。

コメント