Срезы массива в C#
как вы это делаете? Учитывая массив байтов:
byte[] foo = new byte[4096];
как я могу получить первые X байт массива в виде отдельного массива? (В частности, мне это нужно как IEnumerable<byte>)
Это для работы с Sockets. Я считаю, что самый простой способ-это нарезка массива, аналогичная синтаксису Perls:
@bar = @foo[0..40];
который вернет первые 41 элемент в @bar массив. Есть ли что-то в C#, что мне просто не хватает, или есть какая-то другая вещь, которой я должен быть делать?
LINQ-это вариант для меня (.NET 3.5), если это поможет.
16 ответов:
массивы перечислимы, поэтому ваш
fooужеIEnumerable<byte>сам по себе. Просто используйте методы последовательности LINQ, такие какTake()чтобы получить то, что вы хотите из него (не забудьте включитьLinqпространство имен сusing System.Linq;):byte[] foo = new byte[4096]; var bar = foo.Take(41);Если вам действительно нужен массив из любой
IEnumerable<byte>значение, вы можете использоватьToArray()способ для этого. Здесь, похоже, это не так.
вы могли бы использовать
ArraySegment<T>. Это очень легкий вес, так как он не копирует массив:string[] a = { "one", "two", "three", "four", "five" }; var segment = new ArraySegment<string>( a, 1, 2 );
вы могли бы использовать массивы
CopyTo()метод.или с LINQ вы можете использовать
Skip()иTake()...byte[] arr = {1, 2, 3, 4, 5, 6, 7, 8}; var subset = arr.Skip(2).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);
еще одна возможность, которую я не видел здесь: буфер.BlockCopy() немного быстрее, чем массив.Copy (), и у него есть дополнительное преимущество-возможность конвертировать на лету из массива примитивов (скажем, short []) в массив байтов, что может быть удобно, когда у вас есть числовые массивы, которые вам нужно передать через сокеты.
вот простой метод расширения, который возвращает срез в виде нового массива:
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; }затем вы можете использовать его как:
byte[] slice = foo.Slice(0, 40);
вы можете использовать оболочку вокруг исходного массива (который является IList), как в этом (непроверенном) фрагменте кода.
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}
Если вы не хотите, чтобы добавить LINQ или другие расширения просто делают:
float[] subArray = new List<float>(myArray).GetRange(0, 8).ToArray();
вы можете использовать метод расширения Take
var array = new byte[] {1, 2, 3, 4}; var firstTwoItems = array.Take(2);
Это может быть решение, которое:
var result = foo.Slice(40, int.MaxValue);тут результат это IEnumerable> С IEnumerable содержит первые 40 байт фу, а второй IEnumerable держится все остальное.
Я написал класс-оболочку, вся итерация ленивая, надеюсь, что это может помочь:
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; }
на C# 7.2, вы можете использовать
Span<T>. Благо новоеSystem.Memoryсистема заключается в том, что он не нуждается в копировании данных.метод вам нужен
Slice:Span<byte> slice = foo.Slice(0, 40);многие методы теперь поддерживают
SpanиIReadOnlySpan, так что это будет очень просто использовать этот новый тип.обратите внимание, что на момент написания
Span<T>тип еще не определен в самой последней версии .NET (4.7.1) поэтому для его использования вам необходимо установить
вот функция расширения, которая использует универсальный и ведет себя как функция PHP array_slice. Допускается отрицательное смещение и длина.
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; } }
Я не думаю, что C# поддерживает семантику диапазона. Вы можете написать метод расширения, например:
public static IEnumerator<Byte> Range(this byte[] array, int start, int end);но, как и другие сказали, Если вам не нужно устанавливать начальный индекс, то
Takeвсе, что вам нужно.
Comments