ArraySegment <T>类有什么用?

sta*_*ser 89 .net c# arrays

我只是在对ArraySegment<byte>类进行子MessageEncoder类化时遇到了这种类型.

我现在明白它是给定数组的一部分,取一个偏移量,不可枚举,并且没有索引器,但我仍然无法理解它的用法.有人可以用一个例子解释一下吗?

Ste*_*edy 48

ArraySegment<T>在.NET 4.5中已经变得非常有用,因为它现在实现了:

  • IList<T>
  • ICollection<T>
  • IEnumerable<T>
  • IEnumerable
  • IReadOnlyList<T>
  • IReadOnlyCollection<T>

而不是没有实现任何接口的.NET 4版本.

该类现在能够参与LINQ的精彩世界,因此我们可以执行常见的LINQ操作,例如查询内容,反转内容而不影响原始数组,获取第一项,等等:

var array = new byte[] { 5, 8, 9, 20, 70, 44, 2, 4 };
array.Dump();
var segment = new ArraySegment<byte>(array, 2, 3);
segment.Dump(); // output: 9, 20, 70
segment.Reverse().Dump(); // output 70, 20, 9
segment.Any(s => s == 99).Dump(); // output false
segment.First().Dump(); // output 9
array.Dump(); // no change
Run Code Online (Sandbox Code Playgroud)

  • 尽管他们莫名其妙地将`GetEnumerator`设为私有,这意味着您不得不强制转换为`IEnumerable &lt;T&gt;`(拳击转换)来调用它。啊! (4认同)

Gre*_*lty 24

  1. IO类的缓冲区分配 - 使用相同的缓冲区进行同时读写操作,并且具有一个可以传递的单一结构来描述整个操作.
  2. 设置函数 - 从数学上讲,您可以使用此新结构表示任何连续的子集.这基本上意味着你可以创建数组的分区,但你不能代表所有的赔率和所有的平均值.请注意,使用ArraySegment分区和树结构可以优雅地解决The1提出的电话预告片.可以通过首先遍历树深度来写出最终的数字.我相信这在内存和速度方面是理想的情况.
  3. 多线程 - 您现在可以生成多个线程以在同一数据源上运行,同时使用分段数组作为控制门.使用离散计算的循环现在可以很容易地进行修复,这是最新的C++编译器开始作为代码优化步骤所做的事情.
  4. UI分段 - 使用分段结构约束UI显示.您现在可以存储表示可以快速应用于显示功能的数据页面的结构.可以使用单个连续数组,以通过将线性数据存储分段为节点集合段来显示离散视图,甚至是分层结构,例如TreeView中的节点.

在本例中,我们将介绍如何使用原始数组,Offset和Count属性,以及如何循环遍历ArraySegment中指定的元素.

using System;

class Program
{
    static void Main()
    {
        // Create an ArraySegment from this array.
        int[] array = { 10, 20, 30 };
        ArraySegment<int> segment = new ArraySegment<int>(array, 1, 2);

        // Write the array.
        Console.WriteLine("-- Array --");
        int[] original = segment.Array;
        foreach (int value in original)
        {
            Console.WriteLine(value);
        }

        // Write the offset.
        Console.WriteLine("-- Offset --");
        Console.WriteLine(segment.Offset);

        // Write the count.
        Console.WriteLine("-- Count --");
        Console.WriteLine(segment.Count);

        // Write the elements in the range specified in the ArraySegment.
        Console.WriteLine("-- Range --");
        for (int i = segment.Offset; i < segment.Count+segment.Offset; i++)
        {
            Console.WriteLine(segment.Array[i]);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

ArraySegment结构 - 他们在想什么?

  • ArraySegment只是一个结构.我最好的猜测是它的目的是允许传递一个数组的一部分,而不必复制它. (3认同)
  • 当您使用别人的代码时,通常适合给予归因.这只是礼貌.您的示例源自http://www.dotnetperls.com/arraysegment. (3认同)

Han*_*ant 20

这是一个微不足道的小士兵结构,除了保持对数组的引用并存储索引范围之外什么都不做.有点危险,请注意它不会复制数组数据,也不会以任何方式使数组不可变或表达对不可变性的需求.更典型的编程模式是保持或传递数组和长度变量或参数,就像在.NET BeginRead()方法,String.SubString(),Encoding.GetString()等中完成的那样.

它在.NET Framework中没有多大用处,除了看起来像一个特定的Microsoft程序员在Web套接字和WCF喜欢它.这可能是正确的指导,如果你喜欢它然后使用它.它确实在.NET 4.6中做了一个嘘声,添加的MemoryStream.TryGetBuffer()方法使用它.out我假设有两个参数的首选.

一般来说,更普遍的切片概念在主要.NET工程师的心愿表中很重要,如Mads Torgersen和Stephen Toub.后者array[:]在不久前启动了语法提议,您可以在Roslyn页面中看到他们一直在思考的内容.我认为获得CLR支持是最终取决于它的原因.这是积极考虑C#版本7 afaik,请密切关注System.Slices.

更新:死链接,这在版本7.2中作为Span发布.

Update2:使用Range和Index类型以及Slice()方法在C#8.0版中提供更多支持.

  • 好吧,好吧,我并不需要每个养成使用它习惯的人的见证:)最好还是@vice的评论.如上所述,"如果你喜欢它,那就使用它".所以使用它.切片会很棒,不能等待. (4认同)
  • “它不是很有用” - 我发现它在一个不幸的是由于内存限制而需要微优化的系统中非常有用。事实上,还有*还有*其他“典型”解决方案,但这并不会减损它的实用性 (2认同)

ner*_*eia 7

什么是包装类?只是为了避免将数据复制到临时缓冲区.

public class SubArray<T> {
        private ArraySegment<T> segment;

        public SubArray(T[] array, int offset, int count) {
            segment = new ArraySegment<T>(array, offset, count);
        }
        public int Count {
            get { return segment.Count; }
        }

        public T this[int index] {
            get {
               return segment.Array[segment.Offset + index];
            }
        }

        public T[] ToArray() {
            T[] temp = new T[segment.Count];
            Array.Copy(segment.Array, segment.Offset, temp, 0, segment.Count);
            return temp;
        }

        public IEnumerator<T> GetEnumerator() {
            for (int i = segment.Offset; i < segment.Offset + segment.Count; i++) {
                yield return segment.Array[i];
            }
        }
    } //end of the class
Run Code Online (Sandbox Code Playgroud)

例:

byte[] pp = new byte[] { 1, 2, 3, 4 };
SubArray<byte> sa = new SubArray<byte>(pp, 2, 2);

Console.WriteLine(sa[0]);
Console.WriteLine(sa[1]);
//Console.WriteLine(b[2]); exception

Console.WriteLine();
foreach (byte b in sa) {
    Console.WriteLine(b);
}
Run Code Online (Sandbox Code Playgroud)

输出继电器:

3
4

3
4
Run Code Online (Sandbox Code Playgroud)


小智 5

ArraySegment比您想象的更有用.尝试运行以下单元测试,并准备惊讶!

    [TestMethod]
    public void ArraySegmentMagic()
    {
        var arr = new[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

        var arrSegs = new ArraySegment<int>[3];
        arrSegs[0] = new ArraySegment<int>(arr, 0, 3);
        arrSegs[1] = new ArraySegment<int>(arr, 3, 3);
        arrSegs[2] = new ArraySegment<int>(arr, 6, 3);
        for (var i = 0; i < 3; i++)
        {
            var seg = arrSegs[i] as IList<int>;
            Console.Write(seg.GetType().Name.Substring(0, 12) + i);
            Console.Write(" {");
            for (var j = 0; j < seg.Count; j++)
            {
                Console.Write("{0},", seg[j]);
            }
            Console.WriteLine("}");
        }
    }
Run Code Online (Sandbox Code Playgroud)

你知道,你所要做的就是将一个ArraySegment转换为IList,它将完成你可能希望它首先做的所有事情.请注意,类型仍然是ArraySegment,即使它的行为类似于普通列表.

OUTPUT:

ArraySegment0 {0,1,2,}
ArraySegment1 {3,4,5,}
ArraySegment2 {6,7,8,}
Run Code Online (Sandbox Code Playgroud)

  • 遗憾的是有必要将它转换为`IList <T>`.我希望索引器是`public`. (4认同)
  • 对于任何遇到这个答案并认为它是一个奇迹解决方案的人,我建议首先考虑您的性能需求,并与使用数组段中的索引约束直接访问原始数组相比,对此进行基准测试。转换为 IList 需要后续方法调用(包括索引器)在到达实现之前跳过 IList 接口。互联网上有很多讨论,人们谈论在紧密循环中使用抽象调用的性能成本。在这里阅读:https://github.com/dotnet/coreclr/issues/9105 (2认同)

M.M*_*our 5

简而言之:它保留对数组的引用,允许您对单个数组变量进行多个引用,每个引用都有不同的范围。

事实上,它可以帮助您以更结构化的方式使用和传递数组的各个部分,而不是使用多个变量来保存起始索引和长度。它还提供了集合接口,可以更轻松地处理数组部分。

例如,以下两个代码示例执行相同的操作,一个使用 ArraySegment,另一个不使用 ArraySegment:

byte[] arr1 = new byte[] { 1, 2, 3, 4, 5, 6 };
ArraySegment<byte> seg1 = new ArraySegment<byte>(arr1, 2, 2);
MessageBox.Show((seg1 as IList<byte>)[0].ToString());
Run Code Online (Sandbox Code Playgroud)

和,

byte[] arr1 = new byte[] { 1, 2, 3, 4, 5, 6 };
int offset = 2;
int length = 2;
byte[] arr2 = arr1;
MessageBox.Show(arr2[offset + 0].ToString());
Run Code Online (Sandbox Code Playgroud)

显然第一个代码片段是更优选的,特别是当您想要将数组段传递给函数时。