tags:

views:

5693

answers:

10
+12  Q: 

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.

+5  A: 

You could use the arrays CopyTo() method...

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

EDIT2: Usage of LINQ Skip and Take:

byte[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
var subset = arr.Skip(2).Take(2);
Arjan Einbu
+1 for a good idea, but I need to use the returned array as input for another function, which makes CopyTo require a temporary variable. I'll wait for other answers yet.
Matthew Scharley
I'm not familiar with LINQ yet, perhaps this is further evidence that I really should be.
Matthew Scharley
this approach is at least 50x slower than Array.Copy. This isn't an issue in many situations but when doing array slicing in a cycle, the performance drop is very obvious.
Valentin Vasiliev
A: 

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.

bleevo
+6  A: 

If you want IEnumerable<byte>, then just

IEnumerable<byte> data = foo.Take(x);
Marc Gravell
+1  A: 

You can use Take extension method

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

Arrays are enumerable, so your foo already is an IEnumerable<byte> itself. Simply use LINQ sequence methods like Take() to get what you want out of it:

byte[] foo = new byte[4096];

var bar = foo.Take(41);

If you really need an array from any IEnumerable<byte> value, you could use the ToArray() method for that. Here that does not seem to be the case.

peSHIr
If we are going to copy to another array just use the Array.Copy static method. However I think the other answers have interpreted the intent correctly, another array is not required just an IEnumberable<byte> that over the first 41 bytes.
AnthonyWJones
Note that only single dimensional and jagged arrays are enumerable, multi dimensional arrays are not.
Abel
+3  A: 

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

}

Rauhotz
I'd suggest using EqualityComparer.Default for IndexOf - that way you don't need any special casing.
Jon Skeet
How's the performance of EqualityComparer.Default?
Rauhotz
@Rauhotz: I'd expect it to be absolutely fine. I'd certainly go with the simpler code first.
Jon Skeet
+8  A: 
int slicelength = 41;
int startoffset = 0;
byte[] foo = new byte[4096];
byte[] destfoo = new byte[slicelength];
Array.Copy(foo, startoffset, destfoo, 0, slicelength);
WOPR
+15  A: 

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 );
Mike Scott
Unfortunately it's not IEnumerable.
recursive
True, but it would be easy to write an iterator wrapper around it that implements IEnumerable.
Mike Scott
+1 I didn't even know that type existed!
ShdNx
+1  A: 

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.

Ken Smith
+1  A: 

byte[] foo = new byte[4096];

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

greyline