Получение подмассива из существующего массива - PullRequest
292 голосов
/ 03 июня 2009

У меня есть массив X из 10 элементов. Я хотел бы создать новый массив, содержащий все элементы из X, которые начинаются с индекса 3 и заканчиваются индексом 7. Конечно, я могу легко написать цикл, который сделает это для меня, но я бы хотел сохранить свой код как можно более чистым , Есть ли в C # метод, который может сделать это для меня?

Что-то вроде (псевдокод):

Array NewArray = oldArray.createNewArrayFromRange(int BeginIndex , int EndIndex)

Array.Copy не соответствует моим потребностям . Мне нужно, чтобы элементы в новом массиве были клонами. Array.copy это просто эквивалент C-Style memcpy, это не то, что я ищу.

Ответы [ 22 ]

421 голосов
/ 03 июня 2009

Вы можете добавить его как метод расширения:

public static T[] SubArray<T>(this T[] data, int index, int length)
{
    T[] result = new T[length];
    Array.Copy(data, index, result, 0, length);
    return result;
}
static void Main()
{
    int[] data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    int[] sub = data.SubArray(3, 4); // contains {3,4,5,6}
}

Обновление повторного клонирования (что не было очевидно в исходном вопросе). Если вы действительно хотите глубокого клона; что-то вроде:

public static T[] SubArrayDeepClone<T>(this T[] data, int index, int length)
{
    T[] arrCopy = new T[length];
    Array.Copy(data, index, arrCopy, 0, length);
    using (MemoryStream ms = new MemoryStream())
    {
        var bf = new BinaryFormatter();
        bf.Serialize(ms, arrCopy);
        ms.Position = 0;
        return (T[])bf.Deserialize(ms);
    }
}

Для этого требуется, чтобы объекты были сериализуемыми ([Serializable] или ISerializable). Вы можете легко заменить любой другой сериализатор соответствующим образом - XmlSerializer, DataContractSerializer, protobuf-net и т. Д.

Обратите внимание, что глубокий клон сложен без сериализации; в частности, ICloneable трудно доверять в большинстве случаев.

281 голосов
/ 03 июня 2009

Вы можете использовать Array.Copy(...) для копирования в новый массив после того, как вы его создали, но я не думаю, что есть метод, который создает новые массивы и копий ряд элементов.

Если вы используете .NET 3.5, вы могли бы использовать LINQ:

var newArray = array.Skip(3).Take(5).ToArray();

но это будет несколько менее эффективно.

См. этот ответ на аналогичный вопрос для вариантов для более конкретных ситуаций.

66 голосов
/ 14 июля 2009

Рассматривали ли вы использовать ArraySegment?

http://msdn.microsoft.com/en-us/library/1hsbd92d.aspx

31 голосов
/ 19 декабря 2011

Следующий код делает это в одной строке:

// Source array
string[] Source = new string[] { "A", "B", "C", "D" };
// Extracting a slice into another array
string[] Slice = new List<string>(Source).GetRange(2, 2).ToArray();
29 голосов
/ 07 июля 2009

Я вижу, вы хотите делать клонирование, а не просто копировать ссылки. В этом случае вы можете использовать .Select для проецирования элементов массива на их клонов. Например, если ваши элементы реализовали IClonable, вы могли бы сделать что-то вроде этого:

var newArray = array.Skip(3).Take(5).Select(eachElement => eachElement.Clone()).ToArray();

Примечание. Для этого решения требуется .NET Framework 3.5.

12 голосов
/ 03 июня 2014
string[] arr = { "Parrot" , "Snake" ,"Rabbit" , "Dog" , "cat" };

arr = arr.ToList().GetRange(0, arr.Length -1).ToArray();
8 голосов
/ 08 июля 2009

Опираясь на ответ Марка, но добавляя желаемое поведение клонирования

public static T[] CloneSubArray<T>(this T[] data, int index, int length)
    where T : ICloneable
{
    T[] result = new T[length];
    for (int i = 0; i < length; i++)
    { 
        var original = data[index + i];
        if (original != null)
            result[i] = (T)original.Clone();            
    return result;
}

А если реализация ICloneable слишком похожа на тяжелую работу, то на рефлексивную, используя Копируемую библиотеку Ховарда Страндена для выполнения необходимых тяжелых работ.

using OX.Copyable;

public static T[] DeepCopySubArray<T>(
    this T[] data, int index, int length)
{
    T[] result = new T[length];
    for (int i = 0; i < length; i++)
    { 
        var original = data[index + i];
        if (original != null)
            result[i] = (T)original.Copy();            
    return result;
}

Обратите внимание, что реализация OX.Copyable работает с любым из:

Для того, чтобы автоматизированное копирование работало, должно быть выполнено, например, одно из следующих утверждений:

  • Его тип должен иметь конструктор без параметров, или
  • Это должен быть Копируемый, или
  • Для его типа должен быть зарегистрирован IInstanceProvider.

Так что это должно охватывать практически любую ситуацию, которая у вас есть. Если вы клонируете объекты, где подграф содержит такие вещи, как соединения БД или дескрипторы файла / потока, у вас, очевидно, есть проблемы, но это верно для любой обобщенной глубокой копии.

Если вы хотите использовать какой-то другой подход глубокого копирования, в этой статье перечислены несколько других , поэтому я бы предложил не пытаться писать свой собственный.

8 голосов
/ 03 июня 2009

Вы можете сделать это довольно легко;

    object[] foo = new object[10];
    object[] bar = new object[7];   
    Array.Copy(foo, 3, bar, 0, 7);  
4 голосов
/ 03 июня 2009

Я думаю, что код, который вы ищете:

Array.Copy(oldArray, 0, newArray, BeginIndex, EndIndex - BeginIndex)

3 голосов
/ 25 января 2019

В C # 8 введены новые типы Range и Index

int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Index i1 = 3;  // number 3 from beginning
Index i2 = ^4; // number 4 from end
var slice = a[i1..i2]; // { 3, 4, 5 }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...