質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

2回答

3708閲覧

C#のArraySegmentとMemoryの違いは何でしょうか?

fijino

総合スコア136

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

1グッド

0クリップ

投稿2020/10/26 10:39

編集2020/10/26 11:19

ArraySegment<T> Memory<T>の違いは何でしょうか?

基本的に同じ事が出来るように思うのですが、どういう使い分けをするのでしょうか?

C#

1var bytes = new byte[] { 0x01, 0x02, 0x03 }; 2var arraySegemtn = new ArraySegment<byte>(bytes); 3 4for(var i=0; i<arraySegemtn.Count; i++) 5{ 6 System.Diagnostics.Debug.WriteLine(arraySegemtn[i]); 7} 8var array2 = arraySegemtn.Slice(2, 1);

C#

1var bytes = new byte[] { 0x01, 0x02, 0x03 }; 2var memory = new Memory<byte>(bytes); 3for (var i = 0; i < memory.Length; i++) 4{ 5 System.Diagnostics.Debug.WriteLine(memory.Span[i]); 6} 7var memory2 = memory.Slice(2, 1);

追記

コメントでSpanも同時に質問した方が良いとのアドバイスを受けました。
確かに、Spanも同じ事が出来るようですが、これら3つはどういう使い分けをするのでしょうか。

C#

1var bytes = new byte[] { 0x01, 0x02, 0x03 }; 2var span = new Span<byte>(bytes); 3for (var i = 0; i < span.Length; i++) 4{ 5 System.Diagnostics.Debug.WriteLine(span[i]); 6} 7var span2= span.Slice(2, 1);
退会済みユーザー👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Zuishin

2020/10/26 10:44

Span<T> も同時に聞いた方が良いのではないでしょうか。
fijino

2020/10/26 11:20

確かにSpan<T>も同じ事が出来るようなので、質問を追記しました。 もし、ご存知であれば教えて頂けないでしょうか。
guest

回答2

0

ベストアンサー

こんにちは。

まず一言で違いを述べると、昔からある遅いのが ArraySegment<T>、新しくて速いのが Span<T>/Memory<T> です。

最近の .NET ではパフォーマンスへの影響の少ない API への需要が高まっていて、今までは unsafe を使って危険な操作をしないと実現できなかったようなメモリフレンドリな操作を一部 safe な文脈で書けるようにしました。
具体的には、ヒープやスタックを問わず「連続したメモリ領域」への操作を可能にするためのものを作りました。これが Span<T> で、内部は生ポインタと範囲という非常にローレベルなものです。これを C# から safe に操作するために、言語側にもランタイム側にも様々な対応を行いようやく実装されました。
Span<T> は連続したメモリ領域を扱う場合には最速ですが、safe を保証するための非常に強い特別な制約を持ちます。例えば、クラスのフィールドに置けない、スコープ外に持ち出しができない、ジェネリック型引数に入れられないなど。
パフォーマンスをやや犠牲にこの制約を緩めたものが Memory<T> です。Span と似た API を持ち、Span より取り扱いを容易くしたものと考えてください。
また、読み取り専用であることを明示することでできる操作を増やした ReadOnlySpan<T>/ReadOnlyMemory<T> というものもあります。例えば、ReadOnly なら string を「変更不可能な連続したメモリ領域」とみなして扱うことができます。

ArraySegment<T> は素朴に実装された配列の切り抜きなので、シンプルに古い機能だけで操作できます。Span でしかできないことは多いですが、制約により Span<T> にはできないことも色々あるため、依然として使い分けが成立します。
とはいえ、ArraySegment<T> の API は非常に古いので、使わなくて済む場面では利用は避けたほうが良いでしょう。

まとめると、
もし、それぞれで同じ操作が書けるシーンであれば、とりあえず Span<T> を使っておけば良いと思います。
制約に引っかかるようだったら Memory<T> を、目的の操作が実現できない場合に ArraySegment<T> の利用を検討すれば良いです。
読み取り専用で良ければ ReadOnlySpan<T>/ReadOnlyMemory<T> を使うことで操作可能な対象を増やしたりできます。

投稿2020/10/27 01:04

tamoto

総合スコア4105

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fijino

2020/10/27 08:51

わかりました。ご回答ありがとうございます。
guest

0

解決済みの過去の質問に対する回答で恐縮ですが、tamotoさんの回答ではArraySegment<T>を使う時が無いかのように書かれていますのでコメントさせて頂きます。
ただし、私もArraySegmentの使用を推奨しない立場です。

byte/charを使うならSpan<T>/Memory<T>というのはそうです。
構造体の配列等で利用するケースでは、ArraySegmentの方が適合するでしょう。

名前の通り、配列を部分的な範囲で切り出したい場合ArraySegmentを使います。
ArraySegment<T>IEnumerabe<T>を実装しており、切り出された配列に対してLinq等の操作が使えます。
巨大な配列から子配列をコピーすることなく部分的な操作を行うことができます。

しかし、IEnumerable<T>としてみると、部分の切り出し自体はSkipTakeでも可能です。

ArraySegment<T>IEnumerable<T>の最大の違いは何か。
それは、元配列の書き換えが可能という点です。
ArraySegmentは元配列のラッパーであり、元配列側の保持データを書き換えることができます。
しかし、ステートフルな構造体プールはプログラムの複雑さを上げる危険行為です。
そのような機構が必要なプログラムは、C#のターゲットには現状ほぼ存在しないでしょう。
この特性は、不必要な強力すぎる機能と言えます。
ReadOnlyMemorystringの代入を許すように、基本はimmutableがベースであるべきです。

ですから、ArraySegmentを利用することは通常、推奨できません。
強いて言えば、配列限定でSkip+Takeより明快なコーディングができる、と、それぐらいのものであると思います。

私の場合、多量の作業が登録されたイベントを、指定量毎の作業に分割するために使っています。
イベントがデータ転送を伴うもので、せめてオブジェクトを再分割することを避けようという最後の抵抗ですね。

投稿2021/09/11 05:34

haru666

総合スコア1591

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問