Ioan
Ioan

Reputation: 2412

List with dynamically generated elements

I have a function that returns a read-only list of values. In some cases, this list of values can grow very large and can be described by an algorithm. I would like to have the list generate those values dynamically as requested rather than generate a list with all of them. Is there anything generic or a template or anything else that can get me started should I need to implement more than the algorithm myself?

I realize the idea itself is rather generic, but I tagged it for my specific case as a starting point.

Upvotes: 1

Views: 47

Answers (2)

JPortillo
JPortillo

Reputation: 551

As dasblinkenlight mentions, List has to hold the elements. As defined in the interfaces it implements, IList and ICollection. IEnumerable is your only choice if you want to return something that represents your value list. Check this implementation.

public class FunctionBasedList<T> : IEnumerable<T>
{
    public class Enumerator<T> : IEnumerator<T>
    {
        private int index;
        private T current;

        private FunctionBasedList<T> list;

        internal Enumerator(FunctionBasedList<T> list)
        {
            this.list = list;
        }

        public T Current {
            get
            {
                return current;
            }
        }

        Object IEnumerator.Current
        {
            get
            {
                if (index == 0 || index == this.list.Count + 1)
                {
                    throw new InvalidOperationException();
                }

                return current;
            }
        }

        public bool MoveNext()
        {
            if (list.Count > index)
            {
                current = this.list[index];
                index++;
                return true;
            }

            return false;
        }

        public void Reset()
        {
            index = 0;
            current = default(T);
        }

        public void Dispose()
        {

        }
    }

    public int Count
    {
        private set;
        get;
    }

    public Func<int, T> Function
    {
        private set;
        get;
    }

    public T this[int index]
    {
        get
        {
            if (index < 0 || index >= this.Count)
            {
                throw new IndexOutOfRangeException();
            }

            return this.Function(index);
        }
    }

    public FunctionBasedList(int count, Func<int, T> function)
    {
        this.Count = count;
        this.Function = function;
    }

    public Enumerator<T> GetEnumerator()
    {
        return new Enumerator<T>(this);
    }

    IEnumerator<T> IEnumerable<T>.GetEnumerator()
    {
        return new Enumerator<T>(this);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return new Enumerator<T>(this);
    }
}

class Program
{
    static void Main(string[] args)
    {
        FunctionBasedList<int> readOnlyList = new FunctionBasedList<int>(
            10,
            (index) =>
            {
                //Arbitrary function to generate a specific value for a specific index
                return index * 100;
            });

        foreach (int r in readOnlyList)
        {
            Console.WriteLine(r);
        }

        //Loop through a subset of the list
        for (int i = 5; i < 8; i++)
        {
            Console.WriteLine(readOnlyList[i]);
        }

        //Will throw an IndexOutOfRangeException
        Console.WriteLine(readOnlyList[10]);
    }
}

You can return IEnumerator and use List<T> or FunctionBasedList<T> for different cases.

Upvotes: 0

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726579

Since List<T> represents an in-memory collection, there is no way to generate its members dynamically without storing them in memory.

However, you can do it with an IEnumerable<T>: use a yield return statement in a loop to generate the items of a sequence dynamically:

public IEnumerable<int> RandomlyIncreasing() {
    var rnd = new Random();
    var current = rnd.Next(0, 50);
    for (int i = 0 ; i != 1000000 ; i++) {
        yield return current;
        current += rnd.Next(0, 10);
    }
}

Demo.

Upvotes: 2

Related Questions