ArraySegment - возврат фактического сегмента C # - PullRequest
14 голосов
/ 22 апреля 2011

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

 byte[] input = new byte[5]{1,2,3,4,5};
 ArraySegment<byte> delimited = new ArraySegment<byte>(input,0,2);
 byte[] segment = HERE I NEED SOMETHING THAT WILL RETURN THE SEGMENT i.e. [0,1,2]

Самый важный момент, сегмент не должен быть копией, а должен ссылаться на исходный массив. Если какие-либо изменения в сегменте сделаны, они должны быть отражены в исходном массиве.

Любые советы высоко ценятся, спасибо!

ЭТАЛОН НАЗНАЧЕНИЯ : после некоторых ответов от Томас и digEmAll

Хорошо, я выполнил несколько тестов для кода от digEmAll и Thomas, и, к моему удивлению, код работает намного быстрее. Именно то, что я отчаянно искал. Вот результаты.

Construct             Size    Elements assigned    Iterations       Time
_______________________________________________________________________________

ArraySegmentWrapper   1500        1500              1000000       396.3 ms
Array.Copy            1500        1500              1000000       4389.04 ms

Как вы можете видеть колоссальную разницу, мне очень ясно, что я буду использовать код для ArraySegment. Ниже приведен код тестирования. Обратите внимание, что это может быть немного предвзято, как люди будут спорить, почему «новый» был помещен в цикл Я просто пытаюсь воспроизвести ситуацию, которая у меня есть, и решить ее как можно больше, не перемещая большую часть кода. Это просто сделало мой день!

namespace ArraySegmentWrapped
{
    class Program
    {

        public static Stopwatch stopWatch = new Stopwatch();
        public static TimeSpan span = new TimeSpan();
        public static double totalTime = 0.0;
        public static int iterations = 1000000;

        static void Main(string[] args)
        {
            int size = 1500;
            int startIndex = 0;
            int endIndex = 1499;
            byte[] array1 = new byte[size];
            byte[] array2 = null;

            for (int index = startIndex; index < size; index++)
            {
                array1[index] = (byte)index;
            }

            ArraySegmentWrapper<byte> arraySeg;

            for (int index = 0; index < iterations; index++)
            {
                stopWatch.Start();
                arraySeg = new ArraySegmentWrapper<byte>(array1, startIndex, endIndex);            
                stopWatch.Stop();
                totalTime += stopWatch.Elapsed.TotalMilliseconds;
            }

            Console.WriteLine("ArraySegment:{0:F6}", totalTime / iterations);
            stopWatch.Reset();
            totalTime = 0.0;

            for (int index = 0; index < iterations; index++)
            {
                stopWatch.Start();
                array2 = new byte[endIndex - startIndex + 1];
                Array.Copy(array1, startIndex, array2, 0, endIndex);
                stopWatch.Stop();
                totalTime += stopWatch.Elapsed.TotalMilliseconds;
            }
            Console.WriteLine("Array.Copy:{0:F6}", totalTime / iterations);                        


        }
    }
// Code for ArraySegmentWrapper goes here    

}

ЭТАЛОНЫ ДОСТУПА (Обновлено) Итак, после того, что Томас указал на тесты и сказал, что доступ к простым массивам будет быстрее по сравнению с ArraySegment, он был совершенно прав. Но с digEmAll указав, что я должен тестировать в режиме выпуска (извините за старую ошибку тестирования в режиме отладки), я оставил код почти таким же, как и выше (итерации сокращены на два нуля - не мог ждать очень долго для вывода на давай, извини) и некоторые модификации для доступа к тому же количеству элементов, вот что я получил.

Construct             Size    Elements accessed    Iterations       Time
_______________________________________________________________________________

ArraySegmentWrapper   1500        1500              1000000       5268.3 ms
Array.Copy            1500        1500              1000000       4812.4 ms

Сделан вывод о том, что, несмотря на то, что задание выполняется очень быстро, доступ через ArraySegments замедляется.

Ответы [ 4 ]

8 голосов
/ 22 апреля 2011

Начиная с Предложение Томаса Левеска Я создал простой ArraySegmentWrapper<T> класс для использования таким образом:

static void Main(string[] args)
{
    int[] arr = new int[10];
    for (int i = 0; i < arr.Length; i++)
        arr[i] = i;

    // arr = 0,1,2,3,4,5,6,7,8,9

    var segment = new ArraySegmentWrapper<int>(arr, 2, 7);
    segment[0] = -1;
    segment[6] = -1;
    // now arr = 0,1,-1,3,4,5,6,7,-1,9


    // this prints: -1,3,4,5,6,7,-1
    foreach (var el in segment)
        Console.WriteLine(el);
}

Реализация:

public class ArraySegmentWrapper<T> : IList<T>
{
    private readonly ArraySegment<T> segment;

    public ArraySegmentWrapper(ArraySegment<T> segment)
    {
        this.segment = segment;
    }

    public ArraySegmentWrapper(T[] array, int offset, int count)
        : this(new ArraySegment<T>(array, offset, count))
    {
    }

    public int IndexOf(T item)
    {
        for (int i = segment.Offset; i < segment.Offset + segment.Count; i++)
            if (Equals(segment.Array[i], item))
                return i;
        return -1;
    }

    public void Insert(int index, T item)
    {
        throw new NotSupportedException();
    }

    public void RemoveAt(int index)
    {
        throw new NotSupportedException();
    }

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

    public void Add(T item)
    {
        throw new NotSupportedException();
    }

    public void Clear()
    {
        throw new NotSupportedException();
    }

    public bool Contains(T item)
    {
        return this.IndexOf(item) != -1;
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        for (int i = segment.Offset; i < segment.Offset + segment.Count; i++)
        {
            array[arrayIndex] = segment.Array[i];
            arrayIndex++;
        }
    }

    public int Count
    {
        get { return this.segment.Count; }
    }

    public bool IsReadOnly
    {
        get { return false; }
    }

    public bool Remove(T item)
    {
        throw new NotSupportedException();
    }

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

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

РЕДАКТИРОВАТЬ:

Как указал @JeppeStigNielsen в комментариях, поскольку .NET 4.5 ArraySegment<T> реализует IList<T>

5 голосов
/ 22 апреля 2011

Я использую следующий набор методов расширения для работы с сегментами массива:

    #region ArraySegment related methods

    public static ArraySegment<T> GetSegment<T>(this T[] array, int from, int count)
    {
        return new ArraySegment<T>(array, from, count);
    }

    public static ArraySegment<T> GetSegment<T>(this T[] array, int from)
    {
        return GetSegment(array, from, array.Length - from);
    }

    public static ArraySegment<T> GetSegment<T>(this T[] array)
    {
        return new ArraySegment<T>(array);
    }

    public static IEnumerable<T> AsEnumerable<T>(this ArraySegment<T> arraySegment)
    {
        return arraySegment.Array.Skip(arraySegment.Offset).Take(arraySegment.Count);
    }

    public static T[] ToArray<T>(this ArraySegment<T> arraySegment)
    {
        T[] array = new T[arraySegment.Count];
        Array.Copy(arraySegment.Array, arraySegment.Offset, array, 0, arraySegment.Count);
        return array;
    }

    #endregion

Вы можете использовать их следующим образом:

byte[] input = new byte[5]{1,2,3,4,5};
ArraySegment<byte> delimited = input.GetSegment(0, 2);
byte[] segment = delimited.ToArray();
2 голосов
/ 22 апреля 2011

C # (и .NET в целом) не позволяет создавать стандартную ссылку на массив, которая «указывает» на внутреннюю часть другого массива.Таким образом, вам нужно либо изменить свои API-интерфейсы-потребители, чтобы они могли работать с экземплярами ArraySegment, либо вам нужно создать копию данных и затем скопировать изменения обратно после работы с этой копией.В любом случае это, как правило, более безопасный подход, поскольку передача ссылок на массив нарушает изоляцию и затрудняет поиск ошибок по мере увеличения числа потребителей массива.Создание новых экземпляров массива и копирование значений относительно дешево в .NET, если массивы не очень велики по размеру, поэтому влияние на производительность здесь, как правило, незначительно.

Если у вас проблемы с производительностью и вам необходимо микрооптимизировать, я бы порекомендовал использовать либо небезопасный код C # (где вы можете исправить ссылку на массив и обойти указатели), либо извлекать критичные для производительностикод для сборки C ++ / CLI, где вы можете выполнять вычисления с неуправляемой памятью.Я бы рекомендовал сначала профилировать код, чтобы убедиться, что это действительно ваше узкое место.Я не могу не подчеркнуть, что вы не должны бояться выделять новую память в .NET, так как природа кучи сжатия GC означает, что частые небольшие выделения дешевле, чем в C (где распределение памяти должно быть приспособлено для возможной фрагментации кучи)..)

1 голос
/ 20 мая 2015

Ознакомьтесь с ответом, который я разместил по этой теме здесь .

В основном все, что вам нужно сделать, - привести ArraySegment к IList, чтобы получить ожидаемую функциональность.

...