当前位置:  开发笔记 > 编程语言 > 正文

C#中的数组切片

如何解决《C#中的数组切片》经验,为你挑选了14个好方法。

你怎么做呢?给定一个字节数组:

byte[] foo = new byte[4096];

如何将数组的前x个字节作为单独的数组?(具体来说,我需要它作为一个IEnumerable)

这是为了与Sockets合作.我认为最简单的方法是数组切片,类似于Perls语法:

@bar = @foo[0..40];

这会将前41个元素返回到@bar数组中.C#中有些东西我只是缺少,还是还有其他一些我应该做的事情?

LINQ对我来说是一个选项(.NET 3.5),如果这有帮助的话.



1> Mike Scott..:

你可以用ArraySegment.它的重量非常轻,因为它不会复制数组:

string[] a = { "one", "two", "three", "four", "five" };
var segment = new ArraySegment( a, 1, 2 );


ArraySegment是从.Net 4.5开始的IList和IEnumerable.对于旧版本用户来说太糟糕了..
有谁知道为什么它不是IEnumerable?我不.看起来应该是这样.
@Zyo我的意思是ArraySegment 从.Net 4.5开始实现IEnumerable ,而不是IEnumerable 本身是新的.
不幸的是,它不是IEnumerable.

2> peSHIr..:

数组是可枚举的,所以你foo已经是一个IEnumerable本身.只需使用LINQ序列方法Take()就可以得到你想要的东西(不要忘记包含Linq命名空间 using System.Linq;):

byte[] foo = new byte[4096];

var bar = foo.Take(41);

如果你真的需要任何IEnumerable值的数组,你可以使用这个ToArray()方法.这似乎不是这种情况.


使用Array.Copy的注意事项比使用LINQ的Take或Skip方法快得多.
如果我们要复制到另一个数组,只需使用Array.Copy静态方法.但是我认为其他答案已正确解释了意图,另外一个数组不需要只有前31个字节的IEnumberable .
@Abel这实际上是非常不正确的.多维数组*是*可枚举的,但它们枚举如下:`[2,3] => [1,1],[1,2],[1,3],[2,1],[2,2] ,[2,3]`.锯齿状数组也是可枚举的,但它们不是在枚举时返回值,而是返回它们的内部数组.像这样:`type [] [] jaggedArray; foreach(在jaggedArray中键入[] innerArray){}`
@Aidiakapi"非常直接"?).但你部分正确,我应该写"多字节数组不实现`IEnumerable `",然后我的陈述会更清楚.另见:http://stackoverflow.com/questions/721882/multidimensional-arrays-do-not-implement-ienumerablet-or-do-they
请注意,只有单维和锯齿状数组是可枚举的,而多维数组则不是.

3> Arjan Einbu..:

您可以使用数组CopyTo()方法.

或者使用LINQ,您可以使用Skip()Take()...

byte[] arr = {1, 2, 3, 4, 5, 6, 7, 8};
var subset = arr.Skip(2).Take(2);


这种方法比Array.Copy慢至少50倍.这在许多情况下不是问题,但在循环中进行数组切片时,性能下降非常明显.
我还不熟悉LINQ,也许这是我真正应该做的进一步证据.
我打了一个电话,所以演出对我来说不是问题.这非常适合阅读......谢谢.
谢谢你的"Skip()".只是`Take()`不会让你获得任意切片.此外,我一直在寻找一个LINQ解决方案(切片IEnumerable,但我知道有关数组的结果会更容易找到).

4> WOPR..:
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);


我认为Buffer.BlockCopy()效率更高,并获得相同的结果.

5> Remy_rm..:

从C#8.0 / .Net Core 3.0开始

将支持数组切片,IndexRange添加新的类型。

范围结构文档
索引结构文档

Index i1 = 3;  // number 3 from beginning
Index i2 = ^4; // number 4 from end
int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Console.WriteLine($"{a[i1]}, {a[i2]}"); // "3, 6"

var slice = a[i1..i2]; // { 3, 4, 5 }

上面的代码示例摘自C#8.0 博客。

请注意,^前缀表示从数组末尾开始计数。如docs示例所示

var words = new string[]
{
                // index from start    index from end
    "The",      // 0                   ^9
    "quick",    // 1                   ^8
    "brown",    // 2                   ^7
    "fox",      // 3                   ^6
    "jumped",   // 4                   ^5
    "over",     // 5                   ^4
    "the",      // 6                   ^3
    "lazy",     // 7                   ^2
    "dog"       // 8                   ^1
};              // 9 (or words.Length) ^0

Range并且Index还可以在切片数组之外工作,例如使用循环

Range range = 1..4; 
foreach (var name in names[range])

将遍历条目1至4


请注意,在编写此答案时,C#8.0尚未正式发布


似乎是副本:https://www.codejourney.net/2019/02/csharp-8-slicing-indexes-ranges/

6> Ken Smith..:

我在这里没有提到的另一种可能性:Buffer.BlockCopy()比Array.Copy()略快,它还具有能够从原始数组中即时转换的好处(比如说,简短) [])到一个字节数组,当你需要通过套接字传输数字数组时,它可以很方便.


@jocull - 他们实际上并没有采用相同的参数.Array.Copy()在元素中获取其长度和位置参数.Buffer.BlockCopy()以字节为单位获取其长度和位置参数.换句话说,如果你想复制一个10元素的整数数组,你可以使用`Array.Copy(array1,0,array2,0,10)`,但是`Buffer.BlockCopy(array1,0,array2,0) ,10*sizeof(int))`.
`Buffer.BlockCopy`产生的结果与`Array.Copy()`不同,即使它们接受相同的参数 - 有很多空元素.为什么?

7> Patrick Hofm..:

在C#7.2中,您可以使用Span.新System.Memory系统的好处是它不需要复制数据.

你需要的方法是Slice:

Span slice = foo.Slice(0, 40);

很多方法现在支持SpanIReadOnlySpan,所以这将是非常简单的使用这个新的类型.

请注意,在编写本文时,Span尚未在最新版本的.NET(4.7.1)中定义类型,因此要使用它,您需要从NuGet 安装System.Memory包.



8> Vladimir Mit..:

这是一个简单的扩展方法,它将切片作为新数组返回:

public static T[] Slice(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);



9> Marc Gravell..:

如果你想IEnumerable,那就是

IEnumerable data = foo.Take(x);



10> Dimitris..:

如果您不想添加LINQ或其他扩展,请执行以下操作:

float[] subArray = new List(myArray).GetRange(0, 8).ToArray();



11> Rauhotz..:

您可以在原始数组(IList)周围使用包装器,就像在这个(未经测试的)代码段中一样.

public class SubList : IList
{
    #region Fields

private readonly int startIndex;
private readonly int endIndex;
private readonly int count;
private readonly IList source;

#endregion

public SubList(IList source, int startIndex, int count)
{
    this.source = source;
    this.startIndex = startIndex;
    this.count = count;
    this.endIndex = this.startIndex + this.count - 1;
}

#region IList 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 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 Members

public IEnumerator 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

}


我建议使用EqualityComparer.Default for IndexOf - 这样你就不需要任何特殊的套管了.

12> 小智..:
byte[] foo = new byte[4096]; 

byte[] bar = foo.Take(40).ToArray();



13> Simon Giles..:

对于字节数组,System.Buffer.BlockCopy将为您提供最佳性能。



14> aku..:

您可以使用Take扩展方法

var array = new byte[] {1, 2, 3, 4};
var firstTwoItems = array.Take(2);

推荐阅读
罗文彬2502852027
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有