ArraySegment Structure - what were they thinking?

From the MSDN docs:
ArraySegment is a wrapper around an array that delimits a range of elements in that array. Multiple ArraySegment instances can refer to the same original array and can overlap.

Turns out this structure doesn't even deserve the definition 'wrapper'. It simply takes the array, offset and number of elements in your segment, and sets a few properties accordingly.

Subsequently when you want to iterate over the items in your ArraySegment, you still need to use a combination of Offset and Count to achieve this. How is this different from not creating an ArraySegment and define your Offset in-situ as well as the number of elements?

I was expecting to be able to do something like this:

ArraySegment<string> seg = new ArraySegment<string>(new string[] { "John","Jack","Jill","Joe"},1,2);
// Access first item in segment
string first = seg[0];
// Iterate through ArraySegment
foreach (string s in seg)
{
    Console.WriteLine(s);
}


Turns out you can't. There's no indexer for ArraySegment and no enumerator. You have to access the .Array property and use .Count and .Offset as passed to the constructor. What is the point of that!?

So I rolled my own generic DelimitedArray class which does exactly that. See the inline code below.

    public class DelimitedArray<T>
    {
        public DelimitedArray(T[] array, int offset, int count)
        {
            this._array = array;
            this._offset = offset;
            this._count = count;
        }

        private int _offset;
        private T[] _array;

        private int _count;
        public int Count
        {
            get { return this._count; }
        }

        public T this[int index]
        {
            get
            {
                int idx = this._offset + index;
                if (idx > this.Count - 1 || idx<0)
                {
                    throw new IndexOutOfRangeException("Index '" + idx + "' was outside the bounds of the array.");
                }
                return this._array[idx];
            }
        }

        public IEnumerator<T> GetEnumerator()
        {
            for (int i = this._offset; i < this._offset + this.Count; i++)
            {
                yield return this._array[i];
            }
        }
    }


Hope this is of use to someone.

15 Comments

  • It is correctly named a 'wrapper' you were propable expecting a 'decorator'.


  • Gabriel - the main point I'm making is, the ArraySegment 'wrapper' is totally useless!!! Which is why I rolled the DelimitedArray class.

  • It looks like your indexer is off by one:

    int idx = this._offset + index - 1;

    If offset is 1 (starting from the second element of the array) and index is zero (returning the first element of the section), you will return the first element of the array, not the second.

  • Good catch Richard!

    Fixed now...

  • It is more of a container class to allow passing Lists of ArraySegments to a method. For example, the Socket class now allows passing ArraySegments so you can get scatter/gather semantics. You could not pass parcial buffers without something like ArraySegment and would need to copy to buffer of exact size. It is probably most useful for byte[]s, but it did not really cost anything to make it generic - so I guess that why it is generic.

  • However, I had the exact same problem... and had to roll my own DelimitedArray, which looks very much like Wim's.

  • How is this faster than the base .NET implementation? Sure, it is convenient to use, but the very first issue I see is you declared this as a "class" as opposed to .NET's implementation which declares it as a "struct."

  • I doubt there may be bug in you ArraySegment.
    If the size of the intenal array is 10 and you create a ArraySegment object with Count 12, what may happen?

  • Thanks, a had same problem

  • You have a tough crowd you are trying to please, judging from some of the comments.

    I too think that there are already thousands of vocabulary words to remember when dealing with dot net and C#. A program has to learn more new words during their education than a foreign language student. One has to ask if it was really required to add another nonsense word in order to add this functionality.

    I agree with you. It is complication that is not worth it.

  • By changing T[] into IEnumerable and changing this._array[idx] into this._array.ElementAt(idx) you can actually pass almost any list or array into it and still get your segment/part/delimited array of elements.

  • I had to add The non-generic GetEnumerator as well to get this to work:

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

  • Awesome article, you really nailed it.

  • let's say you want to pass a function multiple arrays and offsets and lengths... look for example socket.BeginSend: you don't want to copy all the values to a new array because it spends time and memory, therefore, you send List of ArraySegments.

  • Looks like the finally fixed it in .NET 4.5!

Comments have been disabled for this content.