IEnumerable <T>Использование ToArray - это копия или указатель? - PullRequest
3 голосов
/ 21 апреля 2010

Я анализирую байтовый массив произвольной длины, который будет передан нескольким различным слоям синтаксического анализа. Каждый анализатор создает заголовок и полезную нагрузку пакета, как любая обычная инкапсуляция.

Моя проблема заключается в том, как инкапсуляция удерживает полезную нагрузку массива байтов пакетов. Скажем, у меня есть 100-байтовый массив с тремя уровнями инкапсуляции. Будут созданы три объекта пакета, и я хочу установить полезную нагрузку этих пакетов в соответствующую позицию в байтовом массиве пакета.

Например, допустим, что размер полезной нагрузки равен 20 для всех уровней, а затем представьте, что на каждом объекте есть public byte[] Payload. Однако проблема в том, что byte[] Payload является копией исходных 100 байт, поэтому в итоге я получу 160 байт в памяти вместо 100.

Если бы это было в C ++, я мог бы просто использовать указатель - однако я пишу это в C #.

Итак, я создал следующий класс:

public class PayloadSegment<T> : IEnumerable<T>
{
    public readonly T[] Array;
    public readonly int Offset;
    public readonly int Count;

    public PayloadSegment(T[] array, int offset, int count)
    {
        this.Array = array;
        this.Offset = offset;
        this.Count = count;
    }

    public T this[int index]
    {
        get
        {
            if (index < 0 || index >= this.Count)
                throw new IndexOutOfRangeException();
            else
                return Array[Offset + index];
        }
        set
        {
            if (index < 0 || index >= this.Count)
                throw new IndexOutOfRangeException();
            else
                Array[Offset + index] = value;
        }
    }

    public IEnumerator<T> GetEnumerator()
    {
        for (int i = Offset; i < Offset + Count; i++)
            yield return Array[i];
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        IEnumerator<T> enumerator = this.GetEnumerator();
        while (enumerator.MoveNext())
        {
            yield return enumerator.Current;
        }
    }
}

Таким образом, я могу просто ссылаться на позицию внутри исходного байтового массива, но использовать позиционную индексацию. Однако, если я сделаю что-то вроде:

 PayloadSegment<byte> something = new PayloadSegment<byte>(someArray, 5, 10);
 byte[] somethingArray = something.ToArray();

Будет ли somethingArray копией байтов или ссылкой на исходный PayloadSegment (который, в свою очередь, является ссылкой на исходный массив байтов)?

РЕДАКТИРОВАТЬ: На самом деле после переосмысления я не могу просто использовать новый MemoryStream(array, offset, length)?

Ответы [ 5 ]

5 голосов
/ 21 апреля 2010

В документации для метода расширения Enumerable.ToArray конкретно не упоминается, что он делает, когда ему передается последовательность, которая оказывается уже массивом. Но простая проверка с помощью .NET Reflector показывает, что он действительно создает копию массива.

Стоит отметить, однако, что при заданной последовательности, которая реализует ICollection<T> (что делает Array), копирование может быть выполнено намного быстрее, потому что число элементов известно заранее, поэтому не нужно выполнять динамическое изменение размера буфера типа List<T> делает.

1 голос
/ 17 октября 2012

Это копия. Когда вы вызываете метод To<Type>, он создает копию исходного элемента с целевым типом

1 голос
/ 05 июня 2011

Существует очень сильная практика, которая предполагает, что вызов «ToArray» для объекта должен возвращать новый массив, который отделен от чего-либо еще. Ничто из того, что сделано с исходным объектом, не должно влиять на массив, и ничего, что делается с массивом, не должно влиять на исходный объект. Моим личным предпочтением было бы вызвать подпрограмму «ToNewArray», чтобы сделать явным, что каждый вызов будет возвращать новый массив.

Некоторые из моих классов имеют «AsReadableArray», который возвращает массив, который может быть или не быть прикреплен к чему-либо еще. Массив не изменится в ответ на манипуляции с исходным объектом, но возможно, что многократное чтение, дающее одни и те же данные (что они часто будут), вернет один и тот же массив. Мне бы очень хотелось, чтобы .net имел тип ImmutableArray, поддерживающий те же виды операций, что и String [по сути String, являющийся ImmutableArray (Of Char)], и абстрактный тип ReadableArray (от которого наследуются и Array, и ImmutableArray). Я сомневаюсь, что такая вещь могла бы быть втиснута в .Net 5.0, но она позволила бы сделать много вещей намного более аккуратно.

0 голосов
/ 21 апреля 2010

Это копия.Было бы очень не интуитивно понятно, если я передал something.ToArray() некоторому методу, и метод изменил значение something, изменив массив!

0 голосов
/ 21 апреля 2010

Поскольку byte является типом значения, массив будет содержать копии значений, а не указатели на них.
Если вам нужно такое же поведение, как и у ссылочного типа, лучше всего создать класс, который содержит байт, имеет свойство и может группировать другие данные и функции.

...