views:

142

answers:

5

Hi there,

I have got the array containing some data, say, a header and a real data. I need to pass the data contained in the array to a method, but I definitely want to avoid copying it to another array.

I thought of something like ArraySegment, but it seems not to work in my case (or maybe I'm wrong?).

So, how to pass a part of an array to a method, as it was an array itself?

Thank you for your replies!

Cheers

+6  A: 

Skip and Take:

var subArray = array.Skip(5).Take(10);
Darin Dimitrov
Good, but not if he wants to maintain random access by index.
Dan Tao
@Dan Tao, then there's the `ToArray()` extension method.
Darin Dimitrov
@Darin: At that point you fall foul of "I definitely want to avoid copying it to another array".
Jon Skeet
@Jon, I thought that what the OP meant by *I definitely want to avoid copying it to another array* was *I definitely want to avoid cloning the elements*. Or maybe I got it wrong?
Darin Dimitrov
@Dan: In that case that will involve copying all the elements, which could indeed be a big deal.
Jon Skeet
Yup it does work, but it's slower than copying the subarray to another one...
Jamie
@Darin: I don't think that's what it means. If that is the case, then there's no problem at API boundary at all: the method should just take an array, and if the caller needs to pass a slice (with a copy), he'll just create that sliced copy himself and pass it. He clearly wants to be able to pass a slice "by reference".
Pavel Minaev
@Darin, @Jimmy: It's pretty clear to me that OP wants something that directly accesses the source array but in some kind of clean, encapsulated fashion. Copying elements is precisely what he doesn't want, unless I'm mistaken. @Jon: Did you mean for your last comment to be directed at me, or Jimmy? It seems to me that you and I share the same viewpoint on this, unless I'm missing something.
Dan Tao
@Dan: Oops, it was meant to be at Jimmy :)
Jon Skeet
@Jon: I thought you were supposed to be on vacation (er..um.. holiday?). (I thought I might finally be able to answer a C# question here....)
James Curran
@James: I've come back this evening... although I plan on spending most of the evening playing Worms Reloaded :)
Jon Skeet
+2  A: 

From what I can see, you have two options:

  1. Modify the method you're calling (if you have the option). Instead of just accepting an array (or IEnumerable) you could have it accept an array, a start index, and an end index.

  2. Instead of passing an array, pass in an IEnumerable object that enumerates over the desired range in your array (without making a copy of the items in the array). One way to do that would be:

var slice = someArray.Skip(startIndex).Take(endIndex - startIndex); 
Justin Niessner
+2  A: 

If you want to stick to just basic arrays (ie int [] numbers), then the most efficient way is to have your functions take the offset/count directly.

There are lots of IO functions that do something similar:

readData(data, 0, 4);

string readData(byte [] buffer, int offset, int length)

The other option is to use IEnumberable< T > and use skip/take

readData(data.Skip(0).Take(4));

string readData(IEnumerable<byte> buffer)

It's important to remember that in c# you aren't dealing with pointers, you are dealing with objects.

Alan Jackson
A: 

One option is to implement something like ReadOnlyCollection<T> in terms of implementing IList<T> in an immutable fashion, but expose it as a "view" on an existing collection, shifting any index access appropriately (and with an appropriate count etc).

It would probably be quite a handy wrapper class to have around. You would then modify your method to accept an appropriate IList<T> instead of an array.

Jon Skeet
+3  A: 

I had exactly the same idea as Jon Skeet: implement a wrapper around a T[] that provides random access by index, automatically handling the adjustment of indexed access for you.

I threw together a quick implementation just now (skip ahead to the bottom of this answer for a short demo):

public struct ArrayFragment<T> : IList<T>
{
    private T[] _source;
    private int _start;
    private int _count;

    public ArrayFragment(T[] source, int start, int count)
    {
        if (source == null)
        {
            throw new ArgumentNullException("source");
        }
        if (start < 0 || start >= source.Length)
        {
            throw new ArgumentOutOfRangeException("start");
        }
        if (count > source.Length - start)
        {
            throw new ArgumentOutOfRangeException("count");
        }

        _source = source;
        _start = start;
        _count = count;
    }

    public T this[int index]
    {
        get { return _source[_start + index]; }
    }

    public int Count
    {
        get { return _count; }
    }

    public bool Contains(T value)
    {
        int index = Array.IndexOf(_source, value, _start, _count);
        return index != -1;
    }

    public void CopyTo(T[] destination, int index)
    {
        Array.Copy(_source, _start, destination, index, _count);
    }

    public int IndexOf(T value)
    {
        int index = Array.IndexOf(_source, value, _start, _count);
        return index != -1 ? index - _start : -1;
    }

    public IEnumerator<T> GetEnumerator()
    {
        for (int i = 0; i < _count; ++i)
        {
            yield return _source[_start + i];
        }
    }

    #region Explicit Interface Implementation

    // a bunch of explicitly implemented IList<T> members
    // that all throw a NotSupportedException

    #endregion
}

Here's a demo:

int[] numbers = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

try
{
    var fragment = new ArrayFragment<int>(numbers, 2, 5);

    Console.WriteLine("Iterating using foreach: ");
    foreach (int number in fragment)
    {
        Console.WriteLine(number);
    }

    Console.WriteLine("Iterating using for: ");
    for (int i = 0; i < fragment.Count; ++i)
    {
        Console.WriteLine(fragment[i]);
    }

    Console.WriteLine("Index of 4: {0}", fragment.IndexOf(4));
    Console.WriteLine("Index of 1: {0}", fragment.IndexOf(1));
    Console.WriteLine("Index of 9: {0}", fragment.IndexOf(9));
    Console.WriteLine("Index of 7: {0}", fragment.IndexOf(7));
}
catch (Exception ex)
{
    Console.WriteLine(ex.ToString());
}

Console.ReadLine();

Output:

Iterating using foreach:
3
4
5
6
7
Iterating using for:
3
4
5
6
7
Index of 4: 1
Index of 1: -1
Index of 9: -1
Index of 7: 4
Dan Tao