IEnumerable <t> ToArray用法 - 它是副本还是指针?</t>

时间:2010-04-21 04:37:55

标签: c# list arrays ienumerable

我正在解析一个任意长度的字节数组,它将传递给几个不同的解析层。每个解析器都像普通封装一样创建Header和Packet有效负载。

我的问题在于封装如何保持其包字节数组有效负载。假设我有一个包含三级封装的100字节数组。将创建三个数据包对象,我想将这些数据包的有效负载设置为数据包字节数组中的相应位置。

例如,假设所有级别的有效负载大小都是20,那么可以想象它在每个对象上都有public byte[] Payload。但是,问题是这个byte[] Payload是原始100字节的副本,所以我最终会在内存中使用160字节而不是100字节。

如果是在C ++中,我可以轻松使用指针 - 但是,我是用C#编写的。

所以我创建了以下类:

public class PayloadSegment<T> : IEnumerable<T>
{
    public readonly T[] Array;
    public readonly int Offset;
    public readonly int Count;

    public PayloadSegment(T[] array, int offset, int count)
    {
        this.Array = array;
        this.Offset = offset;
        this.Count = count;
    }

    public T this[int index]
    {
        get
        {
            if (index < 0 || index >= this.Count)
                throw new IndexOutOfRangeException();
            else
                return Array[Offset + index];
        }
        set
        {
            if (index < 0 || index >= this.Count)
                throw new IndexOutOfRangeException();
            else
                Array[Offset + index] = value;
        }
    }

    public IEnumerator<T> GetEnumerator()
    {
        for (int i = Offset; i < Offset + Count; i++)
            yield return Array[i];
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        IEnumerator<T> enumerator = this.GetEnumerator();
        while (enumerator.MoveNext())
        {
            yield return enumerator.Current;
        }
    }
}

这样我可以简单地引用原始字节数组中的位置,但使用位置索引。但是,如果我做了类似的事情:

 PayloadSegment<byte> something = new PayloadSegment<byte>(someArray, 5, 10);
 byte[] somethingArray = something.ToArray();

somethingArray是字节的副本,还是对原始PayloadSegment的引用(后者又是对原始字节数组的引用)?

编辑:实际上在重新思考之后,我不能简单地使用新的MemoryStream(array, offset, length)吗?

5 个答案:

答案 0 :(得分:5)

Enumerable.ToArray扩展方法的文档没有特别提到它在传递已经是数组的序列时的作用。但是使用.NET Reflector进行的简单检查表明它确实创建了数组的副本。

值得注意的是,当给定一个实现ICollection<T>的序列(Array执行)时,副本可以更快地完成,因为元素的数量是预先知道的,所以它没有动态调整缓冲区的大小,例如List<T>

答案 1 :(得分:1)

有一种非常强大的做法,它表明在对象上调用“ToArray”应该返回一个与其他任何东西分离的新数组。对原始对象所做的任何操作都不会影响数组,对数组所做的任何操作都不应影响原始对象。我个人的偏好是调用例程“ToNewArray”,明确表示每个调用都会返回一个不同的新数组。

我的一些类有一个“AsReadableArray”,它返回一个数组,可能会或可能不会附加到其他任何内容。数组不会响应对原始对象的操作而改变,但是可能多次读取产生相同的数据(他们经常会这样做)将返回相同的数组。我真的希望.net有一个ImmutableArray类型,支持与String [一个字符串,本质上是一个ImmutableArray(Of Char)]和一个ReadableArray抽象类型(Array和ImmutableArray都将继承)相同的操作。我怀疑这样的事情可以被挤进.Net 5.0,但它可以让很多东西更干净地完成。

答案 2 :(得分:1)

这是一份副本。当您调用To<Type>方法时,它会使用目标类型

创建源元素的副本

答案 3 :(得分:0)

因为byte是值类型,所以数组将保存值的副本,而不是指向它们的指针。
如果您需要与引用类型相同的行为,最好创建一个保存该字节具有属性的类,并可以对其他数据和功能进行分组。

答案 4 :(得分:0)

这是副本。如果我将something.ToArray()传递给某个方法会非常不直观,并且该方法通过更改数组来更改something的值!