简体   繁体   中英

Referencing a part of an array in C#

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

跳过采取

var subArray = array.Skip(5).Take(10);

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.

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

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); 

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM