5

I have a large number of functions accepting input of sub-arrays (let's say, string) in a read-only way.

I used Array.Copy (C++ memmove equivalent from MSDN) as a temporary workaround, but it comes to a serious bottleneck on running speed. Is there any function I can get a sub-array, let's say [4~94] inside [0~99], such that it can be passed to a function x(array[]) as a reference where inside x the array is defined by [0~90]?

3
  • Are you constrained to passing arrays to the functions, or could you convert those to take an IEnumerable? Commented Mar 16, 2015 at 3:03
  • 1
    What problem are you trying to solve? There's probably a more appropriate data structure you could use. Commented Mar 16, 2015 at 3:18
  • I am paritioning a large document (consider it with some format similar to XML) into objects which the whole file is already parsed as a string of array. The functions wrapped around them are accepting an array of string which is supposed to be partition of the document. I would like to keep the array of string structure (to minimize refactoring) and reduce the amount of time used in parsing. Commented Mar 16, 2015 at 4:50

2 Answers 2

7

Unfortunately, arrays don't play nicely with partitioning. The only way to get a sub-array is by creating a copy of the original array, which is where the bottleneck probably is.

If you can modify your functions to take IEnumerable parameters instead of arrays, you can do this easily with LINQ:

string[] values = { "foo", "bar", "baz", "zap" };
IEnumerable<string> subset = values.Skip(1).Take(2);

You could also look into ArraySegment, but this would (again) require changes to your functions' parameter types.

string[] values = { "foo", "bar", "baz", "zap" };
ArraySegment<string> segment = new ArraySegment<string>(values, 1, 2);
Sign up to request clarification or add additional context in comments.

3 Comments

IMHO, ArraySegment is the way to go. And incredibly (well, to me anyway), all these years I didn't even realize that existed. I've been reimplementing the simple wrapper as needed (I even posted one such implementation as an answer here before I saw your mention of ArraySegment). Argh!
@PeterDuniho ArraySegment was much less useful prior to .NET 4.0, since it didn't implement IEnumerable before then. Your answer could be handy in those cases - you should consider undeleting it.
Well, my answer was 4.5-only, due to the use of IReadOnlyList<T>. So not exactly a big step up. But I suppose a brief comment about how to make it IList<T>-compatible could shift it to at least "potentially useful". Thanks.
1

There is no built-in support for this in C#. You can pass by reference an individual element in an array, but not an address of an array segment that could then be interpreted as a 0-based array in managed code.

The obvious solution is to pass an offset and length along with the array. You can make this a bit less painful by wrapping the array in an IReadOnlyList<T> implementation that encapsulates that for you. For example:

class SubsetList<T> : IReadOnlyList<T>
{
    private readonly IReadOnlyList<T> _list;
    private readonly int _offset = offset;
    private readonly int _length = length;

    public SubsetList(IReadOnlyList<T> list, int offset, int length)
    {
         _list = list;
         _offset = offset;
         _length = length;
    }

    public int Count { get { return _length; } }

    public T this[int index]
    {
        get { return _list[offset + index]; }
    }

    public IEnumerator<T> GetEnumerator()
    {
        for (int i = _offset; i < _offset + _length; i++)
        {
            yield return _list[i];
        }
    }

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

Note that the above is not really needed, as it turns out .NET has (and has had since .NET 2.0) the ArraySegment<T> struct, which does the above and more. But prior to .NET 4.0, it seems it didn't implement the features that would make it transparent to use (such as interface implementations and indexers).

So I offer the above as an alternative for those so-constrained. Note that IReadOnlyList<T> is itself new to .NET 4.5, so to use the above with prior versions of .NET, change that to IList<T>, and then implement all of the other members of the interface with a simple throw new NotSupportedException() (except possibly the setter for the indexer).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.