Ссылка на часть массива в C # - PullRequest
4 голосов
/ 30 августа 2010

У меня есть массив, содержащий некоторые данные, скажем, заголовок и реальные данные. Мне нужно передать данные, содержащиеся в массиве, методу, но я определенно хочу избежать копирования их в другой массив.

Я думал о чем-то вроде ArraySegment, но, похоже, в моем случае это не работает (или, может, я ошибаюсь?).

Итак, как передать часть массива в метод, как это был сам массив?

Спасибо за ваши ответы!

Приветствия

Ответы [ 5 ]

6 голосов
/ 30 августа 2010

Пропустить и Взять :

var subArray = array.Skip(5).Take(10);
5 голосов
/ 30 августа 2010

Если вы хотите придерживаться только базовых массивов (т. Е. Целых чисел []), то наиболее эффективным способом будет , чтобы ваши функции принимали смещение / счет непосредственно .

Есть много функций ввода-вывода, которые делают что-то похожее:

readData(data, 0, 4);

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

Другой вариант - использовать IEnumberable и использовать skip / take

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

string readData(IEnumerable<byte> buffer)

Важно помнить, что в c # вы не имеете дело с указателями , вы имеете дело с объектами.

4 голосов
/ 30 августа 2010

У меня была та же идея, что и у Jon Skeet : реализовать обертку вокруг T[], которая обеспечивает произвольный доступ по индексу, автоматически обрабатывая настройку индексированного доступа для вас.

Я только что собрал быструю реализацию (для краткой демонстрации перейдите к концу этого ответа):

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
}

Вот демонстрационная версия:

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

Вывод:

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
3 голосов
/ 30 августа 2010

Из того, что я вижу, у вас есть две опции:

  1. Изменить метод, который вы вызываете (если у вас есть опция).Вместо того, чтобы просто принимать массив (или IEnumerable), вы могли бы принять массив, начальный индекс и конечный индекс.

  2. Вместо передачи массива передайте объект IEnumerableэто перечисляет по желаемому диапазону в вашем массиве (без создания копии элементов в массиве).Один из способов сделать это будет:

var slice = someArray.Skip(startIndex).Take(endIndex - startIndex); 
0 голосов
/ 30 августа 2010

Один из вариантов - реализовать что-то , например ReadOnlyCollection<T>, с точки зрения реализации IList<T> неизменным образом, но представить его как «представление» существующей коллекции, соответственно сместив любой индексный доступ (и с соответствующим количеством и т. д.).

Вероятно, это будет довольно удобный класс-обертка, чтобы иметь вокруг.Затем вы изменили бы свой метод так, чтобы он принимал соответствующий IList<T> вместо массива.

...