类似(伪代码)的东西:
Array NewArray = oldArray.createNewArrayFromRange(int BeginIndex , int EndIndex)
Array.Copy
不适合我的需求我需要新阵列中的项目才能克隆。 Array.copy
只是C风格的memcpy
的等效物,这不是我想要的。#1 楼
您可以将其添加为扩展方法: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
。评论
(显然,使用结束索引而不是长度是一个简单的更改;我发布了“原样”,因为这是更“典型”的用法)
– Marc Gravell♦
09年3月3日在8:39
然后...强硬它不会那样做。...您可能需要使用序列化来实现类似的功能
– Marc Gravell♦
09年3月3日在19:42
请参阅我的答案以获取一些替代方法以及指向多个实现的链接。对子数组执行操作的部分确实很琐碎,您真正想要的是克隆位,这是一个复杂且有点开放的问题,完全取决于您对“正确”行为的期望。
– ShuggyCoUk
09年7月8日在16:17
很好特别要指出的是,ICloneable是不可靠的,因为它曾经存在过。
– Marcus Griep
09年7月14日在2:02
感谢您强调C#中的深度克隆问题。真的很遗憾,因为深度复制是一项基本操作。
– Dimitri C.
2011-2-22在8:38
#2 楼
创建新数组后,可以使用Array.Copy(...)
将其复制到新数组中,但是我认为没有一种方法可以创建新数组并复制一系列元素。使用.NET 3.5,您可以使用LINQ:
var newArray = array.Skip(3).Take(5).ToArray();
,但是效率会稍差。
对此类似问题的答案参见针对特定情况的选项。
评论
+1我也喜欢这种变化。乔恩(Jon),您能解释一下为什么效率低吗?
–伊恩·洛克(Ian Roke)
2009年6月3日,9:28
@乔恩:为了解决这个问题,不是“ Take(5)”吗? @Ian:Array.Copy方法不涉及枚举器,最有可能是直接的内存复制...
– Marc Gravell♦
09年6月3日,9:35
@马克:是的。撇去问题太多了:)
–乔恩·斯基特(Jon Skeet)
09年6月3日,9:57
@Ian:LINQ方法引入了两个级别的间接(迭代器),必须显式跳过项目,并且不知道最终数组将有多大。考虑使用200万个元素的数组的后半部分:一种简单的“创建目标数组,复制”方法将只复制所需的块,而不会触及其他元素。 LINQ方法将遍历数组直到到达起点,然后开始获取值,建立缓冲区(增加缓冲区大小并定期复制)。效率低得多。
–乔恩·斯基特(Jon Skeet)
2009年6月3日,9:59
如果5是EndIndexm,则正确的问题是array.Skip(3).Take(5-3 + 1).ToArray();即。 array.Skip(StartIndex).Take(EndIndex-StartIndex + 1).ToArray();
– Klaus78
2014年12月3日下午16:01
#3 楼
您考虑使用ArraySegment
吗? http://msdn.microsoft.com/en-us/library/1hsbd92d.aspx
评论
它可能会满足您的要求,但是它不支持默认的数组语法,也不支持IEnumerable,因此它不是特别干净。
– Alex Black
2009年10月8日15:57
这需要更多的支持。在我自己的实验中,ArraySegment的复制也略快一些(毕竟我将数组用于速度至关重要的东西)。
– nawfal
2012年11月15日下午13:17
@AlexBlack从.NET 4.5开始,它实现了IEnumerable
– p.s.w.g
2013年12月26日下午16:21
您将如何使用ArraySegment回答原始问题?
–克雷格·麦昆(Craig McQueen)
15年3月18日在0:43
@CraigMcQueen-尝试以下单行方法:IList
–skia.heliou
15年7月21日在17:30
#4 楼
我看到您想要克隆,而不仅仅是复制引用。在这种情况下,您可以使用
.Select
将数组成员投影到其克隆中。例如,如果您的元素实现了
IClonable
,则可以执行以下操作:var newArray = array.Skip(3).Take(5).Select(eachElement => eachElement.Clone()).ToArray();
注意:此解决方案需要.NET Framework 3.5。
评论
这样更优雅。
–smwikipedia
2014年3月24日5:56
这正是我想要的。这适用于任何IEnumerable。我可以以最小的麻烦获得IEnumerable,IList,IArray等...,如果需要的话,可以内联。如果不需要深层副本,只需删除“选择”即可。放下“跳过”或“取走”可让我控制范围。或者,我可以将其与SkipWhile和/或TakeWhile混合使用。
–迈克
17年9月14日在0:49
#5 楼
以下代码可以一行完成:// 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();
评论
单行,无需添加Linq。这是我的首选方式。
– Dimitris
15年6月12日在20:11
仍然它没有克隆源...但是无论如何这是一个好方法
– I.G. Pascual
16 Mar 23 '16 at 12:56
它应该克隆源,因为ToArray:(1)创建一个新数组,(2)执行Array.Copy。最后,Source和Slice是两个单独的对象。该方法是正确的,但是,我更喜欢Array.Copy:referencesource.microsoft.com/#mscorlib/system/collections/…
–克劳斯
16/09/4在20:23
#6 楼
在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 }
参考文献:
https://docs.microsoft.com / en-us / dotnet / core / whats-new / dotnet-core-3-0#ranges-and-indices
https://devblogs.microsoft.com/dotnet/building-c-8-0/
#7 楼
string[] arr = { "Parrot" , "Snake" ,"Rabbit" , "Dog" , "cat" };
arr = arr.ToList().GetRange(0, arr.Length -1).ToArray();
#8 楼
基于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åvardStranden的Copyable库来执行所需的繁重工作。 br />请注意,OX.Copyable实现可与以下任何一种一起使用:
要使自动副本起作用,例如,以下语句之一必须保留:
其类型必须具有无参数的构造函数,或者
它必须是可复制的,或者
它必须具有为其类型注册的IInstanceProvider。
因此这应该涵盖几乎您遇到的任何情况。如果要克隆子图包含db连接或文件/流句柄之类的对象的对象,显然会遇到问题,但对于任何通用的深层复制都是如此。
如果要使用其他深层复制方法,请代替本文列出了其他几个,所以我建议不要尝试编写自己的。
评论
第一个可能是所需的解决方案,因为他正在请求克隆。请注意,使用Copy方法时,如果该方法本身已经执行了此操作,则可能甚至不必检查null,因为它是扩展方法。值得一试。
– Dykam
09年7月11日在8:02
是的,我注意到了空检查,但不想混淆OP,以防他不阅读源代码。
– ShuggyCoUk
09年7月11日在11:22
只是一个旁注:GitHub上的最新版本Copyable不需要对象具有无参数构造函数。 :)参见github.com/havard/copyable
–Håvard S
2010-2-2在8:28
#9 楼
您可以相当轻松地完成此操作; object[] foo = new object[10];
object[] bar = new object[7];
Array.Copy(foo, 3, bar, 0, 7);
评论
不,bar仍为空。 Array.Copy不会神奇地创建新的数组,尤其是因为bar不会随ref或out传递。
–Zr40
09年7月8日在16:16
哦,嘿,您的权利,我匆匆忙忙地做了,但是,嘿,也许当您的写作批评成为现实时,建设性的批评对每个人都非常有用。因此在该array.copy之前,您需要执行“ bar = new object [7];”
–RandomNickName42
09年7月11日在7:58
#10 楼
我认为您正在寻找的代码是:Array.Copy(oldArray, 0, newArray, BeginIndex, EndIndex - BeginIndex)
评论
我想我已经在这里交了一些好朋友了。。。。。。。哈哈!无论如何,美好时光美好时光。
–RandomNickName42
09年7月14日在2:41
#11 楼
作为复制数据的替代方法,您可以制作一个包装程序,使您可以访问原始数组的一部分,就好像它是该数组的一部分的副本一样。优点是您不会在内存中获得数据的另一个副本,缺点是访问数据时会产生一些开销。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
评论
@罗伯特:不,不是。尝试改用ArraySegment,您会看到既无法按索引访问项目,也无法遍历项目。
–古法
09年10月8日在15:42
#12 楼
Array.ConstrainedCopy将起作用。public static void ConstrainedCopy (
Array sourceArray,
int sourceIndex,
Array destinationArray,
int destinationIndex,
int length
)
评论
那只是复制数据;它不会创建新的数组等;如果数组是新数组,则可以使用效率更高的Array.Copy(不需要其他检查/回滚)。
– Marc Gravell♦
09年3月3日在8:38
没错,但是创建一个新数组只是一行代码,不需要任何新方法。我同意Array.Copy也可以使用。
–crauscher
09年3月3日在8:48
#13 楼
没有任何一种方法可以满足您的需求。您将需要为数组中的类提供一个克隆方法。然后,如果可以选择LINQ:Foo[] newArray = oldArray.Skip(3).Take(5).Select(item => item.Clone()).ToArray();
class Foo
{
public Foo Clone()
{
return (Foo)MemberwiseClone();
}
}
#14 楼
如何使用Array.ConstrainedCopy:下面是我的原始帖子。它不起作用您可以使用Array.CopyTo:
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);
#15 楼
怎么样: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接口,但这应该可以实现。
#16 楼
我不确定它到底有多深,但是:#17 楼
在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
#18 楼
就克隆而言,我不认为序列化会调用您的构造函数。如果您在ctor中执行有趣的操作,这可能会破坏类不变式。似乎更安全的选择是调用副本构造函数的虚拟克隆方法。
protected MyDerivedClass(MyDerivedClass myClass)
{
...
}
public override MyBaseClass Clone()
{
return new MyDerivedClass(this);
}
评论
序列化是否调用构造函数取决于特定的序列化器。有些会,有些不会。但是那些通常不提供回调支持的功能使您可以进行所需的修复。
– Marc Gravell♦
09年7月14日在4:12
这突出了序列化的另一个障碍:您必须提供默认的构造函数。
–汉斯·马尔赫贝(Hans Malherbe)
09年7月14日在7:32
#19 楼
不能以通用方式克隆数组中的元素。您要深度克隆还是所有成员的简单副本?让我们采取“尽力而为”的方法:使用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);
}
}
}
这不是一个完美的解决方案,因为根本没有一种适用于任何类型的对象。
评论
难道不是应该像result [i-index] =(T)...吗?
–唐纳德·伯德
09年7月13日在20:22
是的:)不仅如此。循环边界错误。我会解决的。谢谢!
– Philippe Leybaert
09年7月13日在20:38
#20 楼
您可以参加Microsoft制作的课程: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));
}
}
#21 楼
我发现这是执行此操作的最佳方法: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]);
}
}
希望有帮助!
#22 楼
使用扩展方法: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);
#23 楼
来自System.Private.CoreLib.dll的代码: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;
}
#24 楼
它不满足您的克隆要求,但似乎比许多答案要简单:Array NewArray = new ArraySegment(oldArray,BeginIndex , int Count).ToArray();
#25 楼
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();
}
评论
重复:stackoverflow.com/questions/406485/array-slices-in-c@Kirtan-“ dup”特别想要IEnumerable
那么,声明新数组并调用.Copy()所需的两行不是“干净的代码”吗?
@Ed Swangren-不需要,如果您需要在链接表达式的中间进行操作,则不行; -p
ShaggyUk的答案可能是正确的答案:stackoverflow.com/questions/943635/…