我如何克隆数组元素的范围到一个新的数组?

我有一个包含10个元素的数组X。我想创建一个新数组,包含X中从索引3开始并在索引7结束的所有元素。当然,我可以很容易地写一个循环,将它为我,但我想保持我的代码尽可能干净。c#中有一个方法可以帮我做这个吗?

类似(伪代码):

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

Array.Copy不适合我的需要。我需要在新数组中的项目是克隆。Array.copy只是一个c风格的memcpy等价物,它不是我要找的。

473287 次浏览

在创建新数组后,可以使用Array.Copy(...)复制到新数组中,但我不认为有创建新数组的方法而且复制一系列元素。

如果你使用。net 3.5,你可以使用LINQ:

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

但这样做效率会低一些。

在更具体的情况下,参见这个答案到类似问题的选项。

你可以添加它作为一个扩展方法:

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)。你可以很容易地替换任何其他合适的序列化器——XmlSerializerDataContractSerializer, protobuf-net等等。

注意,深度克隆在没有序列化的情况下是很棘手的;特别是,ICloneable在大多数情况下很难信任。

数组中。ConstrainedCopy将工作。

public static void ConstrainedCopy (
Array sourceArray,
int sourceIndex,
Array destinationArray,
int destinationIndex,
int length
)

我认为你要找的代码是:

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

这很容易做到;

< a href = " http://ref " rel = " noreferrer ">

    object[] foo = new object[10];
object[] bar = new object[7];
Array.Copy(foo, 3, bar, 0, 7);

作为复制数据的替代方法,您可以制作一个包装器,使您可以访问原始数组的一部分,就像它是该数组部分的副本一样。这样做的优点是不会在内存中获得数据的另一个副本,缺点是在访问数据时产生轻微的开销。

public class SubArray<T> : IEnumerable<T> {


private T[] _original;
private int _start;


public SubArray(T[] original, int start, int len) {
_original = original;
_start = start;
Length = len;
}


public T this[int index] {
get {
if (index < 0 || index >= Length) throw new IndexOutOfRangeException();
return _original[_start + index];
}
}


public int Length { get; private set; }


public IEnumerator<T> GetEnumerator() {
for (int i = 0; i < Length; i++) {
yield return _original[_start + i];
}
}


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


}

用法:

int[] original = { 1, 2, 3, 4, 5 };
SubArray<int> copy = new SubArray<int>(original, 2, 2);


Console.WriteLine(copy.Length); // shows: 2
Console.WriteLine(copy[0]); // shows: 3
foreach (int i in copy) Console.WriteLine(i); // shows 3 and 4

我看到你想做克隆,而不仅仅是复制引用。 在这种情况下,你可以使用.Select将数组成员投射到它们的克隆体中。 例如,如果你的元素实现了IClonable,你可以这样做:

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

注意:此解决方案需要。net Framework 3.5。

以Marc的回答为基础,但加入了想要的克隆行为

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太像艰苦的工作,那么使用Håvard Stranden的Copyable库来完成所需的繁重工作。

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。

所以这应该适用于你遇到的任何情况。如果你克隆的对象子图中包含db连接或文件/流句柄,你显然有问题,但这对任何广义深度复制都是真的。

如果你想使用一些其他的深度复制方法来代替这个文章还列出了其他几个,所以我建议不要尝试自己写。

没有一种方法可以满足你的需求。您需要为数组中的类提供一个克隆方法。然后,如果LINQ是一个选项:

Foo[] newArray = oldArray.Skip(3).Take(5).Select(item => item.Clone()).ToArray();


class Foo
{
public Foo Clone()
{
return (Foo)MemberwiseClone();
}
}

克隆数组中的元素并不是一种通用的方法。您想要深度克隆还是所有成员的简单副本?

让我们采用“尽最大努力”的方法:使用ICloneable接口或二进制序列化克隆对象:

public static class ArrayExtensions
{
public static T[] SubArray<T>(this T[] array, int index, int length)
{
T[] result = new T[length];


for (int i=index;i<length+index && i<array.Length;i++)
{
if (array[i] is ICloneable)
result[i-index] = (T) ((ICloneable)array[i]).Clone();
else
result[i-index] = (T) CloneObject(array[i]);
}


return result;
}


private static object CloneObject(object obj)
{
BinaryFormatter formatter = new BinaryFormatter();


using (MemoryStream stream = new MemoryStream())
{
formatter.Serialize(stream, obj);


stream.Seek(0,SeekOrigin.Begin);


return formatter.Deserialize(stream);
}
}
}

这并不是一个完美的解决方案,因为没有一个解决方案适用于任何类型的对象。

就克隆而言,我认为序列化不会调用构造函数。如果你在ctor中做一些有趣的事情,这可能会破坏类不变量。

似乎更安全的方法是调用复制构造函数的虚拟克隆方法。

protected MyDerivedClass(MyDerivedClass myClass)
{
...
}


public override MyBaseClass Clone()
{
return new MyDerivedClass(this);
}

如何使用数组中。ConstrainedCopy:

int[] ArrayOne = new int[8] {1,2,3,4,5,6,7,8};
int[] ArrayTwo = new int[5];
Array.ConstrainedCopy(ArrayOne, 3, ArrayTwo, 0, 7-3);

以下是我的原帖。这是行不通的

你可以使用数组中。CopyTo:

int[] ArrayOne = new int[8] {1,2,3,4,5,6,7,8};
int[] ArrayTwo = new int[5];
ArrayOne.CopyTo(ArrayTwo,3); //starts copy at index=3 until it reaches end of
//either array

这个怎么样:

public T[] CloneCopy(T[] array, int startIndex, int endIndex) where T : ICloneable
{
T[] retArray = new T[endIndex - startIndex];
for (int i = startIndex; i < endIndex; i++)
{
array[i - startIndex] = array[i].Clone();
}
return retArray;


}

然后,您需要在所有需要使用ICloneable的类上实现ICloneable接口,但这样就可以了。

我不确定它到底有多深,但是:

MyArray.ToList<TSource>().GetRange(beginningIndex, endIndex).ToArray()

这是一些开销,但它可能会减少一个不必要的方法。

下面的代码在一行中完成:

// 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();
string[] arr = { "Parrot" , "Snake" ,"Rabbit" , "Dog" , "cat" };


arr = arr.ToList().GetRange(0, arr.Length -1).ToArray();
public   static   T[]   SubArray<T>(T[] data, int index, int length)
{
List<T> retVal = new List<T>();
if (data == null || data.Length == 0)
return retVal.ToArray();
bool startRead = false;
int count = 0;
for (int i = 0; i < data.Length; i++)
{
if (i == index && !startRead)
startRead = true;
if (startRead)
{


retVal.Add(data[i]);
count++;


if (count == length)
break;
}
}
return retVal.ToArray();
}

你可以参加微软的课程:

internal class Set<TElement>
{
private int[] _buckets;
private Slot[] _slots;
private int _count;
private int _freeList;
private readonly IEqualityComparer<TElement> _comparer;


public Set()
: this(null)
{
}


public Set(IEqualityComparer<TElement> comparer)
{
if (comparer == null)
comparer = EqualityComparer<TElement>.Default;
_comparer = comparer;
_buckets = new int[7];
_slots = new Slot[7];
_freeList = -1;
}


public bool Add(TElement value)
{
return !Find(value, true);
}


public bool Contains(TElement value)
{
return Find(value, false);
}


public bool Remove(TElement value)
{
var hashCode = InternalGetHashCode(value);
var index1 = hashCode % _buckets.Length;
var index2 = -1;
for (var index3 = _buckets[index1] - 1; index3 >= 0; index3 = _slots[index3].Next)
{
if (_slots[index3].HashCode == hashCode && _comparer.Equals(_slots[index3].Value, value))
{
if (index2 < 0)
_buckets[index1] = _slots[index3].Next + 1;
else
_slots[index2].Next = _slots[index3].Next;
_slots[index3].HashCode = -1;
_slots[index3].Value = default(TElement);
_slots[index3].Next = _freeList;
_freeList = index3;
return true;
}
index2 = index3;
}
return false;
}


private bool Find(TElement value, bool add)
{
var hashCode = InternalGetHashCode(value);
for (var index = _buckets[hashCode % _buckets.Length] - 1; index >= 0; index = _slots[index].Next)
{
if (_slots[index].HashCode == hashCode && _comparer.Equals(_slots[index].Value, value))
return true;
}
if (add)
{
int index1;
if (_freeList >= 0)
{
index1 = _freeList;
_freeList = _slots[index1].Next;
}
else
{
if (_count == _slots.Length)
Resize();
index1 = _count;
++_count;
}
int index2 = hashCode % _buckets.Length;
_slots[index1].HashCode = hashCode;
_slots[index1].Value = value;
_slots[index1].Next = _buckets[index2] - 1;
_buckets[index2] = index1 + 1;
}
return false;
}


private void Resize()
{
var length = checked(_count * 2 + 1);
var numArray = new int[length];
var slotArray = new Slot[length];
Array.Copy(_slots, 0, slotArray, 0, _count);
for (var index1 = 0; index1 < _count; ++index1)
{
int index2 = slotArray[index1].HashCode % length;
slotArray[index1].Next = numArray[index2] - 1;
numArray[index2] = index1 + 1;
}
_buckets = numArray;
_slots = slotArray;
}


internal int InternalGetHashCode(TElement value)
{
if (value != null)
return _comparer.GetHashCode(value) & int.MaxValue;
return 0;
}


internal struct Slot
{
internal int HashCode;
internal TElement Value;
internal int Next;
}
}

然后

public static T[] GetSub<T>(this T[] first, T[] second)
{
var items = IntersectIteratorWithIndex(first, second);
if (!items.Any()) return new T[] { };




var index = items.First().Item2;
var length = first.Count() - index;
var subArray = new T[length];
Array.Copy(first, index, subArray, 0, length);
return subArray;
}


private static IEnumerable<Tuple<T, Int32>> IntersectIteratorWithIndex<T>(IEnumerable<T> first, IEnumerable<T> second)
{
var firstList = first.ToList();
var set = new Set<T>();
foreach (var i in second)
set.Add(i);
foreach (var i in firstList)
{
if (set.Remove(i))
yield return new Tuple<T, Int32>(i, firstList.IndexOf(i));
}
}

这是我发现的最佳方法:

private void GetSubArrayThroughArraySegment() {
int[] array = { 10, 20, 30 };
ArraySegment<int> segment = new ArraySegment<int>(array,  1, 2);
Console.WriteLine("-- Array --");
int[] original = segment.Array;
foreach (int value in original)
{
Console.WriteLine(value);
}
Console.WriteLine("-- Offset --");
Console.WriteLine(segment.Offset);
Console.WriteLine("-- Count --");
Console.WriteLine(segment.Count);


Console.WriteLine("-- Range --");
for (int i = segment.Offset; i <= segment.Count; i++)
{
Console.WriteLine(segment.Array[i]);
}
}

希望有帮助!

在c# 8中,他们引入了一个新的RangeIndex类型,它们可以这样使用:

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 }

引用:

使用扩展方法:

public static T[] Slice<T>(this T[] source, int start, int end)
{
// Handles negative ends.
if (end < 0)
{
end = source.Length + end;
}
int len = end - start;


// Return new array.
T[] res = new T[len];
for (int i = 0; i < len; i++)
{
res[i] = source[i + start];
}
return res;
}

你可以使用它

var NewArray = OldArray.Slice(3,7);

来自System.Private.CoreLib.dll的代码:
< / p >

public static T[] GetSubArray<T>(T[] array, Range range)
{
if (array == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
}
(int Offset, int Length) offsetAndLength = range.GetOffsetAndLength(array.Length);
int item = offsetAndLength.Offset;
int item2 = offsetAndLength.Length;
if (default(T) != null || typeof(T[]) == array.GetType())
{
if (item2 == 0)
{
return Array.Empty<T>();
}
T[] array2 = new T[item2];
Buffer.Memmove(ref Unsafe.As<byte, T>(ref array2.GetRawSzArrayData()), ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), item), (uint)item2);
return array2;
}
T[] array3 = (T[])Array.CreateInstance(array.GetType().GetElementType(), item2);
Array.Copy(array, item, array3, 0, item2);
return array3;
}


它不符合你的克隆要求,但它似乎比许多答案要简单:

Array NewArray = new ArraySegment(oldArray,BeginIndex , int Count).ToArray();

在c# 8.0中,你现在可以像Python一样做许多更漂亮的工作,包括反向索引和范围,例如:

int[] list = {1, 2, 3, 4, 5, 6};
var list2 = list[2..5].Clone() as int[]; // 3, 4, 5
var list3 = list[..5].Clone() as int[];  // 1, 2, 3, 4, 5
var list4 = list[^4..^0].Clone() as int[];  // reverse index
array1 = [5,6,7,8];


int[] array2 = new int[2];


Array.ConstrainedCopy(array1, 1, array2, 0, 2);


array2 = [6,7];

数组中。ConstrainedCopy有五个(5)参数:

  1. 源数组(sourceArray)

  2. 源数组起始索引(sourceIndex)

  3. 目标数组(destinationArray)

  4. 目标数组的起始索引(destinationIndex)

  5. 要复制的元素数量(长度)