読者です 読者をやめる 読者になる 読者になる

いろいろ備忘録日記

主に .NET と Python絡みのメモを公開しています。

.NET クラスライブラリ探訪-069 (System.ArraySegment<T>, 部分配列の作成)

今回は、System.ArraySegment構造体についてメモ。

このクラス、.NET 2.0の頃からずっと存在している構造体です。
なにを表す構造体かというと、元となる配列の一部分を表現する部分配列を
構成します。

例えば、[1, 2, 3, 4, 5]という配列があって、その配列の要素2番目から3つ分とか。

結構使いどころによっては便利な型のはずなのですが、この型には歴史が
ありまして、.NET 4.0まで、この構造体は、IEnumerableとかIListとかの
コレクションインターフェースを一切実装していませんでした。

なので、添え字でアクセスできないとか、ループがしづらいなどいろいろ使い勝手が悪い構造体だったのです。

これが、.NET 4.5になって、コレクションインターフェースを実装するように変わりました。
以下のようになっています。

[SerializableAttribute]
public struct ArraySegment<T> : IList<T>, 
    ICollection<T>, IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, 
    IEnumerable

なので、列挙や要素に対してのアクセスもすんなりできるようになっています。

  
以下、サンプルです。

  

namespace Sazare.Samples
{
  using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Threading.Tasks;

  using Sazare.Common;

  /// <summary>
  /// System.ArraySegmentクラスについてのサンプルです。
  /// </summary>
  /// <remarks>
  /// ArraySegment構造体は、.NET 4.0と.NET 4.5以降で
  /// 実装が異なります。.NET 4.5より、この構造体はコレクションインターフェースを
  /// 実装するように変更されています。
  /// </remarks>
  [Sample]
  class ArraySegmentSamples01 : IExecutable
  {
    public void Execute()
    {
      //
      // System.ArraySegmentは、配列の一部分の参照を保持する構造体である。
      // この構造体を利用すると部分配列を手軽に利用できる。
      // (例えば、とある配列の5番目から5つ分など)
      // 
      // System.ArraySegment構造体は、.NET 4.0までと.NET 4.5以降で
      // 実装がかなり異なる。.NET 4.0まで、この型はコレクションインターフェースを
      // 一切実装していなかった。そのため、forでループさせる際にOffsetとCountプロパティ
      // を利用して要素を取得する必要があった。
      // .NET 4.5から、この型はIListやICollectionやIEnumerableなどを実装する
      // ようになったので、foreachなどで手軽にループすることが出来るようになっている。
      // 
      var originalArray = Enumerable.Range(1, 10).ToArray();

      var segment1 = new ArraySegment<int>(originalArray, 0, 5);
      var segment2 = new ArraySegment<int>(originalArray, 5, 5);

      //
      // .NET 4.0まで
      // Offsetプロパティ、Countプロパティを利用してループし要素を取得する.
      //
      for (var i = 0; i < segment1.Count; i++)
      {
        Console.WriteLine(segment1.Array[segment1.Offset + i]);
      }

      for (var i = segment2.Offset; i < (segment2.Offset + segment2.Count); i++)
      {
        Console.WriteLine(segment2.Array[i]);
      }

      //
      // .NET 4.5から
      // 標準のコレクションインターフェースを実装しているため、リストと同じように
      // 扱う事が可能。
      //
      foreach (var item in segment1.Zip(segment2, (x, y) => new { x, y }))
      {
        Console.WriteLine(item);
      }

      //
      // 値の変更
      // 値を変更する場合、予めIList<T>にキャストして変更する。
      // ArraySegmentは、元の配列の要素と同じ参照を保持しているので
      // ArraySegmentの要素の値を変更すると、元の配列の値も変わる。
      //
      var col1 = segment1 as IList<int>;
      col1[2] = 999;

      var col2 = segment2 as IList<int>;
      col2[2] = 888;

      foreach (var item in originalArray)
      {
        Console.WriteLine(item);
      }
    }
  }
}

  

実行すると以下のようになります。

================== START ==================
1
2
3
4
5
6
7
8
9
10
{ x = 1, y = 6 }
{ x = 2, y = 7 }
{ x = 3, y = 8 }
{ x = 4, y = 9 }
{ x = 5, y = 10 }
1
2
999
4
5
6
7
888
9
10
==================  END  ==================

以下、参考にした情報です。


過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。