[c#] Array slices in C#

How do you do it? Given a byte array:

byte[] foo = new byte[4096];

How would I get the first x bytes of the array as a separate array? (Specifically, I need it as an IEnumerable<byte>)

This is for working with Sockets. I figure the easiest way would be array slicing, similar to Perls syntax:

@bar = @foo[0..40];

Which would return the first 41 elements into the @bar array. Is there something in C# that I'm just missing, or is there some other thing I should be doing?

LINQ is an option for me (.NET 3.5), if that helps any.

This question is related to c# arrays

The answer is


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace data_seniens
{
    class Program
    {
        static void Main(string[] args)
        {
            //new list
            float [] x=new float[]{11.25f,18.0f,20.0f,10.75f,9.50f, 11.25f, 18.0f, 20.0f, 10.75f, 9.50f };

            //variable
            float eat_sleep_area=x[1]+x[3];
            //print
            foreach (var VARIABLE in x)
            {
                if (VARIABLE < x[7])
                {
                    Console.WriteLine(VARIABLE);
                }
            }



            //keep app run
        Console.ReadLine();
        }
    }
}

I do not think C# supports the Range semantics. You could write an extension method though, like:

public static IEnumerator<Byte> Range(this byte[] array, int start, int end);

But like others have said if you do not need to set a start index then Take is all you need.


For byte arrays System.Buffer.BlockCopy will give you the very best performance.


Another possibility I haven't seen mentioned here: Buffer.BlockCopy() is slightly faster than Array.Copy(), and it has the added benefit of being able to convert on-the-fly from an array of primitives (say, short[]) to an array of bytes, which can be handy when you've got numeric arrays that you need to transmit over Sockets.


Starting from C# 8.0/.Net Core 3.0

Array slicing will be supported, along with the new types Index and Range being added.

Range Struct docs
Index Struct docs

Index i1 = 3;  // number 3 from beginning
Index i2 = ^4; // number 4 from end
int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Console.WriteLine($"{a[i1]}, {a[i2]}"); // "3, 6"

var slice = a[i1..i2]; // { 3, 4, 5 }

Above code sample taken from the C# 8.0 blog.

note that the ^ prefix indicates counting from the end of the array. As shown in the docs example

var words = new string[]
{
                // index from start    index from end
    "The",      // 0                   ^9
    "quick",    // 1                   ^8
    "brown",    // 2                   ^7
    "fox",      // 3                   ^6
    "jumped",   // 4                   ^5
    "over",     // 5                   ^4
    "the",      // 6                   ^3
    "lazy",     // 7                   ^2
    "dog"       // 8                   ^1
};              // 9 (or words.Length) ^0

Range and Index also work outside of slicing arrays, for example with loops

Range range = 1..4; 
foreach (var name in names[range])

Will loop through the entries 1 through 4


note that at the time of writing this answer, C# 8.0 is not yet officially released
C# 8.x and .Net Core 3.x are now available in Visual Studio 2019 and onwards


You could use ArraySegment<T>. It's very light-weight as it doesn't copy the array:

string[] a = { "one", "two", "three", "four", "five" };
var segment = new ArraySegment<string>( a, 1, 2 );

You can use Take extension method

var array = new byte[] {1, 2, 3, 4};
var firstTwoItems = array.Take(2);

static byte[] SliceMe(byte[] source, int length)
{
    byte[] destfoo = new byte[length];
    Array.Copy(source, 0, destfoo, 0, length);
    return destfoo;
}

//

var myslice = SliceMe(sourcearray,41);

In C# 7.2, you can use Span<T>. The benefit of the new System.Memory system is that it doesn't need copying around data.

The method you need is Slice:

Span<byte> slice = foo.Slice(0, 40);

A lot of methods now support Span and IReadOnlySpan, so it will be very straightforward to use this new type.

Note that at the time of writing the Span<T> type is not defined in the the most recent version of .NET yet (4.7.1) so to use it you need to install the System.Memory package from NuGet.


For byte arrays System.Buffer.BlockCopy will give you the very best performance.


C# 8 now (since 2019) supports Ranges which allows you to achieve Slice much easier (similar to JS syntax):

var array = new int[] { 1, 2, 3, 4, 5 };
var slice1 = array[2..^3];    // array[new Range(2, new Index(3, fromEnd: true))]
var slice2 = array[..^3];     // array[Range.EndAt(new Index(3, fromEnd: true))]
var slice3 = array[2..];      // array[Range.StartAt(2)]
var slice4 = array[..];       // array[Range.All]

You can use ranges instead of the well known LINQ functions: Skip(), Take(), Count().


You could use the arrays CopyTo() method.

Or with LINQ you can use Skip() and Take()...

byte[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
var subset = arr.Skip(2).Take(2);

I do not think C# supports the Range semantics. You could write an extension method though, like:

public static IEnumerator<Byte> Range(this byte[] array, int start, int end);

But like others have said if you do not need to set a start index then Take is all you need.


You can use Take extension method

var array = new byte[] {1, 2, 3, 4};
var firstTwoItems = array.Take(2);

This may be a solution that:

var result = foo.Slice(40, int.MaxValue);

Then the result is an IEnumerable< IEnumerable< byte>> with a first IEnumerable< byte> contains the first 40 bytes of foo, and a second IEnumerable< byte> holds the rest.

I wrote a wrapper class, the whole iteration is lazy, hope it could help:

public static class CollectionSlicer
{
    public static IEnumerable<IEnumerable<T>> Slice<T>(this IEnumerable<T> source, params int[] steps)
    {
        if (!steps.Any(step => step != 0))
        {
            throw new InvalidOperationException("Can't slice a collection with step length 0.");
        }
        return new Slicer<T>(source.GetEnumerator(), steps).Slice();
    }
}

public sealed class Slicer<T>
{
    public Slicer(IEnumerator<T> iterator, int[] steps)
    {
        _iterator = iterator;
        _steps = steps;
        _index = 0;
        _currentStep = 0;
        _isHasNext = true;
    }

    public int Index
    {
        get { return _index; }
    }

    public IEnumerable<IEnumerable<T>> Slice()
    {
        var length = _steps.Length;
        var index = 1;
        var step = 0;

        for (var i = 0; _isHasNext; ++i)
        {
            if (i < length)
            {
                step = _steps[i];
                _currentStep = step - 1;
            }

            while (_index < index && _isHasNext)
            {
                _isHasNext = MoveNext();
            }

            if (_isHasNext)
            {
                yield return SliceInternal();
                index += step;
            }
        }
    }

    private IEnumerable<T> SliceInternal()
    {
        if (_currentStep == -1) yield break;
        yield return _iterator.Current;

        for (var count = 0; count < _currentStep && _isHasNext; ++count)
        {
            _isHasNext = MoveNext();

            if (_isHasNext)
            {
                yield return _iterator.Current;
            }
        }
    }

    private bool MoveNext()
    {
        ++_index;
        return _iterator.MoveNext();
    }

    private readonly IEnumerator<T> _iterator;
    private readonly int[] _steps;
    private volatile bool _isHasNext;
    private volatile int _currentStep;
    private volatile int _index;
}

Another possibility I haven't seen mentioned here: Buffer.BlockCopy() is slightly faster than Array.Copy(), and it has the added benefit of being able to convert on-the-fly from an array of primitives (say, short[]) to an array of bytes, which can be handy when you've got numeric arrays that you need to transmit over Sockets.


byte[] foo = new byte[4096]; 

byte[] bar = foo.Take(40).ToArray();

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace data_seniens
{
    class Program
    {
        static void Main(string[] args)
        {
            //new list
            float [] x=new float[]{11.25f,18.0f,20.0f,10.75f,9.50f, 11.25f, 18.0f, 20.0f, 10.75f, 9.50f };

            //variable
            float eat_sleep_area=x[1]+x[3];
            //print
            foreach (var VARIABLE in x)
            {
                if (VARIABLE < x[7])
                {
                    Console.WriteLine(VARIABLE);
                }
            }



            //keep app run
        Console.ReadLine();
        }
    }
}

static byte[] SliceMe(byte[] source, int length)
{
    byte[] destfoo = new byte[length];
    Array.Copy(source, 0, destfoo, 0, length);
    return destfoo;
}

//

var myslice = SliceMe(sourcearray,41);

If you want IEnumerable<byte>, then just

IEnumerable<byte> data = foo.Take(x);

You could use a wrapper around the original array (which is IList), like in this (untested) piece of code.

public class SubList<T> : IList<T>
{
    #region Fields

    private readonly int startIndex;
    private readonly int endIndex;
    private readonly int count;
    private readonly IList<T> source;

    #endregion

    public SubList(IList<T> source, int startIndex, int count)
    {
        this.source = source;
        this.startIndex = startIndex;
        this.count = count;
        this.endIndex = this.startIndex + this.count - 1;
    }

    #region IList<T> Members

    public int IndexOf(T item)
    {
        if (item != null)
        {
            for (int i = this.startIndex; i <= this.endIndex; i++)
            {
                if (item.Equals(this.source[i]))
                    return i;
            }
        }
        else
        {
            for (int i = this.startIndex; i <= this.endIndex; i++)
            {
                if (this.source[i] == null)
                    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 >= 0 && index < this.count)
                return this.source[index + this.startIndex];
            else
                throw new IndexOutOfRangeException("index");
        }
        set
        {
            if (index >= 0 && index < this.count)
                this.source[index + this.startIndex] = value;
            else
                throw new IndexOutOfRangeException("index");
        }
    }

    #endregion

    #region ICollection<T> Members

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

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

    public bool Contains(T item)
    {
        return this.IndexOf(item) >= 0;
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        for (int i=0; i<this.count; i++)
        {
            array[arrayIndex + i] = this.source[i + this.startIndex];
        }
    }

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

    public bool IsReadOnly
    {
        get { return true; }
    }

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

    #endregion

    #region IEnumerable<T> Members

    public IEnumerator<T> GetEnumerator()
    {
        for (int i = this.startIndex; i < this.endIndex; i++)
        {
            yield return this.source[i];
        }
    }

    #endregion

    #region IEnumerable Members

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

    #endregion
}

In C# 7.2, you can use Span<T>. The benefit of the new System.Memory system is that it doesn't need copying around data.

The method you need is Slice:

Span<byte> slice = foo.Slice(0, 40);

A lot of methods now support Span and IReadOnlySpan, so it will be very straightforward to use this new type.

Note that at the time of writing the Span<T> type is not defined in the the most recent version of .NET yet (4.7.1) so to use it you need to install the System.Memory package from NuGet.


Here's a simple extension method that returns a slice as a new array:

public static T[] Slice<T>(this T[] arr, uint indexFrom, uint indexTo) {
    if (indexFrom > indexTo) {
        throw new ArgumentOutOfRangeException("indexFrom is bigger than indexTo!");
    }

    uint length = indexTo - indexFrom;
    T[] result = new T[length];
    Array.Copy(arr, indexFrom, result, 0, length);

    return result;
}

Then you can use it as:

byte[] slice = foo.Slice(0, 40);

I do not think C# supports the Range semantics. You could write an extension method though, like:

public static IEnumerator<Byte> Range(this byte[] array, int start, int end);

But like others have said if you do not need to set a start index then Take is all you need.


You could use the arrays CopyTo() method.

Or with LINQ you can use Skip() and Take()...

byte[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
var subset = arr.Skip(2).Take(2);

This may be a solution that:

var result = foo.Slice(40, int.MaxValue);

Then the result is an IEnumerable< IEnumerable< byte>> with a first IEnumerable< byte> contains the first 40 bytes of foo, and a second IEnumerable< byte> holds the rest.

I wrote a wrapper class, the whole iteration is lazy, hope it could help:

public static class CollectionSlicer
{
    public static IEnumerable<IEnumerable<T>> Slice<T>(this IEnumerable<T> source, params int[] steps)
    {
        if (!steps.Any(step => step != 0))
        {
            throw new InvalidOperationException("Can't slice a collection with step length 0.");
        }
        return new Slicer<T>(source.GetEnumerator(), steps).Slice();
    }
}

public sealed class Slicer<T>
{
    public Slicer(IEnumerator<T> iterator, int[] steps)
    {
        _iterator = iterator;
        _steps = steps;
        _index = 0;
        _currentStep = 0;
        _isHasNext = true;
    }

    public int Index
    {
        get { return _index; }
    }

    public IEnumerable<IEnumerable<T>> Slice()
    {
        var length = _steps.Length;
        var index = 1;
        var step = 0;

        for (var i = 0; _isHasNext; ++i)
        {
            if (i < length)
            {
                step = _steps[i];
                _currentStep = step - 1;
            }

            while (_index < index && _isHasNext)
            {
                _isHasNext = MoveNext();
            }

            if (_isHasNext)
            {
                yield return SliceInternal();
                index += step;
            }
        }
    }

    private IEnumerable<T> SliceInternal()
    {
        if (_currentStep == -1) yield break;
        yield return _iterator.Current;

        for (var count = 0; count < _currentStep && _isHasNext; ++count)
        {
            _isHasNext = MoveNext();

            if (_isHasNext)
            {
                yield return _iterator.Current;
            }
        }
    }

    private bool MoveNext()
    {
        ++_index;
        return _iterator.MoveNext();
    }

    private readonly IEnumerator<T> _iterator;
    private readonly int[] _steps;
    private volatile bool _isHasNext;
    private volatile int _currentStep;
    private volatile int _index;
}

byte[] foo = new byte[4096]; 

byte[] bar = foo.Take(40).ToArray();

If you don't want to add LINQ or other extensions just do:

float[] subArray = new List<float>(myArray).GetRange(0, 8).ToArray();

static byte[] SliceMe(byte[] source, int length)
{
    byte[] destfoo = new byte[length];
    Array.Copy(source, 0, destfoo, 0, length);
    return destfoo;
}

//

var myslice = SliceMe(sourcearray,41);

If you don't want to add LINQ or other extensions just do:

float[] subArray = new List<float>(myArray).GetRange(0, 8).ToArray();

You could use ArraySegment<T>. It's very light-weight as it doesn't copy the array:

string[] a = { "one", "two", "three", "four", "five" };
var segment = new ArraySegment<string>( a, 1, 2 );

You could use the arrays CopyTo() method.

Or with LINQ you can use Skip() and Take()...

byte[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
var subset = arr.Skip(2).Take(2);

You could use ArraySegment<T>. It's very light-weight as it doesn't copy the array:

string[] a = { "one", "two", "three", "four", "five" };
var segment = new ArraySegment<string>( a, 1, 2 );

You can use Take extension method

var array = new byte[] {1, 2, 3, 4};
var firstTwoItems = array.Take(2);

C# 8 now (since 2019) supports Ranges which allows you to achieve Slice much easier (similar to JS syntax):

var array = new int[] { 1, 2, 3, 4, 5 };
var slice1 = array[2..^3];    // array[new Range(2, new Index(3, fromEnd: true))]
var slice2 = array[..^3];     // array[Range.EndAt(new Index(3, fromEnd: true))]
var slice3 = array[2..];      // array[Range.StartAt(2)]
var slice4 = array[..];       // array[Range.All]

You can use ranges instead of the well known LINQ functions: Skip(), Take(), Count().


I do not think C# supports the Range semantics. You could write an extension method though, like:

public static IEnumerator<Byte> Range(this byte[] array, int start, int end);

But like others have said if you do not need to set a start index then Take is all you need.


static byte[] SliceMe(byte[] source, int length)
{
    byte[] destfoo = new byte[length];
    Array.Copy(source, 0, destfoo, 0, length);
    return destfoo;
}

//

var myslice = SliceMe(sourcearray,41);

You could use the arrays CopyTo() method.

Or with LINQ you can use Skip() and Take()...

byte[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
var subset = arr.Skip(2).Take(2);

If you want IEnumerable<byte>, then just

IEnumerable<byte> data = foo.Take(x);

You could use a wrapper around the original array (which is IList), like in this (untested) piece of code.

public class SubList<T> : IList<T>
{
    #region Fields

    private readonly int startIndex;
    private readonly int endIndex;
    private readonly int count;
    private readonly IList<T> source;

    #endregion

    public SubList(IList<T> source, int startIndex, int count)
    {
        this.source = source;
        this.startIndex = startIndex;
        this.count = count;
        this.endIndex = this.startIndex + this.count - 1;
    }

    #region IList<T> Members

    public int IndexOf(T item)
    {
        if (item != null)
        {
            for (int i = this.startIndex; i <= this.endIndex; i++)
            {
                if (item.Equals(this.source[i]))
                    return i;
            }
        }
        else
        {
            for (int i = this.startIndex; i <= this.endIndex; i++)
            {
                if (this.source[i] == null)
                    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 >= 0 && index < this.count)
                return this.source[index + this.startIndex];
            else
                throw new IndexOutOfRangeException("index");
        }
        set
        {
            if (index >= 0 && index < this.count)
                this.source[index + this.startIndex] = value;
            else
                throw new IndexOutOfRangeException("index");
        }
    }

    #endregion

    #region ICollection<T> Members

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

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

    public bool Contains(T item)
    {
        return this.IndexOf(item) >= 0;
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        for (int i=0; i<this.count; i++)
        {
            array[arrayIndex + i] = this.source[i + this.startIndex];
        }
    }

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

    public bool IsReadOnly
    {
        get { return true; }
    }

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

    #endregion

    #region IEnumerable<T> Members

    public IEnumerator<T> GetEnumerator()
    {
        for (int i = this.startIndex; i < this.endIndex; i++)
        {
            yield return this.source[i];
        }
    }

    #endregion

    #region IEnumerable Members

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

    #endregion
}

Here is an extension function that uses a generic and behaves like the PHP function array_slice. Negative offset and length are allowed.

public static class Extensions
{
    public static T[] Slice<T>(this T[] arr, int offset, int length)
    {
        int start, end;

        // Determine start index, handling negative offset.
        if (offset < 0)
            start = arr.Length + offset;
        else
            start = offset;

        // Clamp start index to the bounds of the input array.
        if (start < 0)
            start = 0;
        else if (start > arr.Length)
            start = arr.Length;

        // Determine end index, handling negative length.
        if (length < 0)
            end = arr.Length + length;
        else
            end = start + length;

        // Clamp end index to the bounds of the input array.
        if (end < 0)
            end = 0;
        if (end > arr.Length)
            end = arr.Length;

        // Get the array slice.
        int len = end - start;
        T[] result = new T[len];
        for (int i = 0; i < len; i++)
        {
            result[i] = arr[start + i];
        }
        return result;
    }
}

Here's a simple extension method that returns a slice as a new array:

public static T[] Slice<T>(this T[] arr, uint indexFrom, uint indexTo) {
    if (indexFrom > indexTo) {
        throw new ArgumentOutOfRangeException("indexFrom is bigger than indexTo!");
    }

    uint length = indexTo - indexFrom;
    T[] result = new T[length];
    Array.Copy(arr, indexFrom, result, 0, length);

    return result;
}

Then you can use it as:

byte[] slice = foo.Slice(0, 40);

You could use ArraySegment<T>. It's very light-weight as it doesn't copy the array:

string[] a = { "one", "two", "three", "four", "five" };
var segment = new ArraySegment<string>( a, 1, 2 );

You could use a wrapper around the original array (which is IList), like in this (untested) piece of code.

public class SubList<T> : IList<T>
{
    #region Fields

    private readonly int startIndex;
    private readonly int endIndex;
    private readonly int count;
    private readonly IList<T> source;

    #endregion

    public SubList(IList<T> source, int startIndex, int count)
    {
        this.source = source;
        this.startIndex = startIndex;
        this.count = count;
        this.endIndex = this.startIndex + this.count - 1;
    }

    #region IList<T> Members

    public int IndexOf(T item)
    {
        if (item != null)
        {
            for (int i = this.startIndex; i <= this.endIndex; i++)
            {
                if (item.Equals(this.source[i]))
                    return i;
            }
        }
        else
        {
            for (int i = this.startIndex; i <= this.endIndex; i++)
            {
                if (this.source[i] == null)
                    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 >= 0 && index < this.count)
                return this.source[index + this.startIndex];
            else
                throw new IndexOutOfRangeException("index");
        }
        set
        {
            if (index >= 0 && index < this.count)
                this.source[index + this.startIndex] = value;
            else
                throw new IndexOutOfRangeException("index");
        }
    }

    #endregion

    #region ICollection<T> Members

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

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

    public bool Contains(T item)
    {
        return this.IndexOf(item) >= 0;
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        for (int i=0; i<this.count; i++)
        {
            array[arrayIndex + i] = this.source[i + this.startIndex];
        }
    }

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

    public bool IsReadOnly
    {
        get { return true; }
    }

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

    #endregion

    #region IEnumerable<T> Members

    public IEnumerator<T> GetEnumerator()
    {
        for (int i = this.startIndex; i < this.endIndex; i++)
        {
            yield return this.source[i];
        }
    }

    #endregion

    #region IEnumerable Members

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

    #endregion
}

Here is an extension function that uses a generic and behaves like the PHP function array_slice. Negative offset and length are allowed.

public static class Extensions
{
    public static T[] Slice<T>(this T[] arr, int offset, int length)
    {
        int start, end;

        // Determine start index, handling negative offset.
        if (offset < 0)
            start = arr.Length + offset;
        else
            start = offset;

        // Clamp start index to the bounds of the input array.
        if (start < 0)
            start = 0;
        else if (start > arr.Length)
            start = arr.Length;

        // Determine end index, handling negative length.
        if (length < 0)
            end = arr.Length + length;
        else
            end = start + length;

        // Clamp end index to the bounds of the input array.
        if (end < 0)
            end = 0;
        if (end > arr.Length)
            end = arr.Length;

        // Get the array slice.
        int len = end - start;
        T[] result = new T[len];
        for (int i = 0; i < len; i++)
        {
            result[i] = arr[start + i];
        }
        return result;
    }
}

Starting from C# 8.0/.Net Core 3.0

Array slicing will be supported, along with the new types Index and Range being added.

Range Struct docs
Index Struct docs

Index i1 = 3;  // number 3 from beginning
Index i2 = ^4; // number 4 from end
int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Console.WriteLine($"{a[i1]}, {a[i2]}"); // "3, 6"

var slice = a[i1..i2]; // { 3, 4, 5 }

Above code sample taken from the C# 8.0 blog.

note that the ^ prefix indicates counting from the end of the array. As shown in the docs example

var words = new string[]
{
                // index from start    index from end
    "The",      // 0                   ^9
    "quick",    // 1                   ^8
    "brown",    // 2                   ^7
    "fox",      // 3                   ^6
    "jumped",   // 4                   ^5
    "over",     // 5                   ^4
    "the",      // 6                   ^3
    "lazy",     // 7                   ^2
    "dog"       // 8                   ^1
};              // 9 (or words.Length) ^0

Range and Index also work outside of slicing arrays, for example with loops

Range range = 1..4; 
foreach (var name in names[range])

Will loop through the entries 1 through 4


note that at the time of writing this answer, C# 8.0 is not yet officially released
C# 8.x and .Net Core 3.x are now available in Visual Studio 2019 and onwards


You could use a wrapper around the original array (which is IList), like in this (untested) piece of code.

public class SubList<T> : IList<T>
{
    #region Fields

    private readonly int startIndex;
    private readonly int endIndex;
    private readonly int count;
    private readonly IList<T> source;

    #endregion

    public SubList(IList<T> source, int startIndex, int count)
    {
        this.source = source;
        this.startIndex = startIndex;
        this.count = count;
        this.endIndex = this.startIndex + this.count - 1;
    }

    #region IList<T> Members

    public int IndexOf(T item)
    {
        if (item != null)
        {
            for (int i = this.startIndex; i <= this.endIndex; i++)
            {
                if (item.Equals(this.source[i]))
                    return i;
            }
        }
        else
        {
            for (int i = this.startIndex; i <= this.endIndex; i++)
            {
                if (this.source[i] == null)
                    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 >= 0 && index < this.count)
                return this.source[index + this.startIndex];
            else
                throw new IndexOutOfRangeException("index");
        }
        set
        {
            if (index >= 0 && index < this.count)
                this.source[index + this.startIndex] = value;
            else
                throw new IndexOutOfRangeException("index");
        }
    }

    #endregion

    #region ICollection<T> Members

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

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

    public bool Contains(T item)
    {
        return this.IndexOf(item) >= 0;
    }

    public void CopyTo(T[] array, int arrayIndex)
    {
        for (int i=0; i<this.count; i++)
        {
            array[arrayIndex + i] = this.source[i + this.startIndex];
        }
    }

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

    public bool IsReadOnly
    {
        get { return true; }
    }

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

    #endregion

    #region IEnumerable<T> Members

    public IEnumerator<T> GetEnumerator()
    {
        for (int i = this.startIndex; i < this.endIndex; i++)
        {
            yield return this.source[i];
        }
    }

    #endregion

    #region IEnumerable Members

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

    #endregion
}

If you want IEnumerable<byte>, then just

IEnumerable<byte> data = foo.Take(x);