multidimensional - Matriz de rodajas en C#
initialize array c# (16)
¿Cómo lo haces? Dado un conjunto de bytes:
byte[] foo = new byte[4096];
¿Cómo obtendría los primeros x bytes de la matriz como una matriz separada? (Específicamente, lo necesito como IEnumerable<byte>
)
Esto es para trabajar con Socket
s. Me imagino que la forma más fácil sería el corte de matrices, similar a la sintaxis de Perls:
@bar = @foo[0..40];
Que devolvería los primeros 41 elementos en la matriz @bar
. ¿Hay algo en C # que me estoy perdiendo, o hay alguna otra cosa que debería estar haciendo?
LINQ es una opción para mí (.NET 3.5), si eso ayuda.
Aquí hay un método de extensión simple que devuelve un sector como una nueva matriz:
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;
}
Entonces puedes usarlo como:
byte[] slice = foo.Slice(0, 40);
Aquí hay una función de extensión que usa un genérico y se comporta como la función PHP array_slice . La compensación y la longitud negativas están permitidas.
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;
}
}
En C # 7.2 , puede usar Span<T>
. El beneficio del nuevo sistema System.Memory
es que no necesita copiar datos.
El método que necesitas es Slice
:
Span<byte> slice = foo.Slice(0, 40);
Muchos métodos ahora son compatibles con Span
e IReadOnlySpan
, por lo que será muy sencillo utilizar este nuevo tipo.
Tenga en cuenta que, en el momento de escribir, el tipo Span<T>
no está definido en la versión más reciente de .NET (4.7.1), por lo que para poder usarlo necesita instalar el paquete System.Memory de NuGet.
Esta puede ser una solución que:
var result = foo.Slice(40, int.MaxValue);
Entonces el resultado es un IEnumerable <IEnumerable <byte >> con un primer IEnumerable <byte> contiene los primeros 40 bytes de foo , y un segundo IEnumerable <byte> contiene el resto.
Escribí una clase contenedora, toda la iteración es floja, espero que pueda ayudar:
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;
}
Las matrices son enumerables, por lo que tu foo
ya es un IEnumerable<byte>
. Simplemente use los métodos de secuencia LINQ como Take()
para obtener lo que quiere de él (no olvide incluir el espacio de nombres de Linq
using System.Linq;
):
byte[] foo = new byte[4096];
var bar = foo.Take(41);
Si realmente necesita una matriz de cualquier valor de IEnumerable<byte>
, podría usar el método ToArray()
para eso. Ese no parece ser el caso aquí.
No creo que C # sea compatible con la semántica de Range. Podría escribir un método de extensión, como:
public static IEnumerator<Byte> Range(this byte[] array, int start, int end);
Pero como otros han dicho, si no necesita establecer un índice de inicio, Take
es todo lo que necesita.
Otra posibilidad que no he visto mencionar aquí: Buffer.BlockCopy () es ligeramente más rápido que Array.Copy (), y tiene el beneficio adicional de poder convertir sobre la marcha de una serie de primitivos (por ejemplo, corto []) a una matriz de bytes, que puede ser útil cuando tienes matrices numéricas que necesitas transmitir sobre Sockets.
Para las matrices de bytes System.Buffer.BlockCopy le proporcionará el mejor rendimiento.
Podría usar el método de arrays CopyTo()
.
O con LINQ puedes usar Skip()
y Take()
...
byte[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
var subset = arr.Skip(2).Take(2);
Podría usar un contenedor alrededor de la matriz original (que es IList), como en este fragmento de código (no probado).
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
}
Puede usar ArraySegment<T>
. Es muy ligero ya que no copia la matriz:
string[] a = { "one", "two", "three", "four", "five" };
var segment = new ArraySegment<string>( a, 1, 2 );
Puedes usar el método de extensión Take
var array = new byte[] {1, 2, 3, 4};
var firstTwoItems = array.Take(2);
Si no desea agregar LINQ u otras extensiones simplemente haga lo siguiente:
float[] subArray = new List<float>(myArray).GetRange(0, 8).ToArray();
Si quieres IEnumerable<byte>
, entonces solo
IEnumerable<byte> data = foo.Take(x);
byte[] foo = new byte[4096];
byte[] bar = foo.Take(40).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);