ArraySegment - Returning the actual segment C#

前端 未结 4 534
野性不改
野性不改 2020-12-10 02:50

I have been looking around on ways to return the segment which is basically held by ArraySegment in terms of offset and count. Although ArraySegment holds the complete and o

相关标签:
4条回答
  • 2020-12-10 03:33

    C# (and .NET in general) doesn't allow you to create a standard array reference that 'points to' the inside of another array. As such, you either need to change your consuming APIs so that they can deal with ArraySegment instances or you need to create a copy of the data and then copy the changes back after operating on the copy. This is generally a safer approach anyway, as passing around references to an array breaks insulation and makes it more difficult to track down bugs as the number of consumers of the array increases. Constructing new array instances and copying values is relatively cheap in .NET, as long as the arrays are not extremely large in size, so the performance impact here is generally negligible.

    If you're running into performance problems and you need to micro-optimize, I would recommend using either unsafe C# code (where you can fix the array reference and pass around pointers) or pulling out the performance-critical code to a C++/CLI assembly where you can do the computations with unmanaged memory. I would recommend profiling the code first to verify that this is really your bottleneck. I can't stress enough that you shouldn't fear allocating new memory in .NET, as the nature of the compacting GC heap means that frequent small allocations are cheaper than they would be in C (where memory allocation must accommodate for possible heap fragmentation.)

    0 讨论(0)
  • 2020-12-10 03:41

    I use the following set of extension methods to work with array segments:

        #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
    

    You can use them as follows:

    byte[] input = new byte[5]{1,2,3,4,5};
    ArraySegment<byte> delimited = input.GetSegment(0, 2);
    byte[] segment = delimited.ToArray();
    
    0 讨论(0)
  • 2020-12-10 03:56

    Starting from Thomas Levesque's suggestion I've built a simple ArraySegmentWrapper<T> class to use in this way:

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

    Implementation:

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

    EDIT :

    As pointed out by @JeppeStigNielsen in the comments, since .NET 4.5 ArraySegment<T> implements IList<T>

    0 讨论(0)
  • 2020-12-10 03:56

    Check out the answer I posted on this topic here.

    Basically all you have to do is cast ArraySegment to IList to get the functionality you expect.

    0 讨论(0)
提交回复
热议问题