注意
前回のコードはref構造体なので,絶対ヒープに乗らず,リストを使用するよりもパフォーマンスが優れているはずですが,今回のコードは微妙です.
ただなんとなく「最大サイズが決まってるリストにリストを使いたくない」という気持ちで書いたもので,パフォーマンスについて検証してません.
使用例
バッファ用の構造体を用意し,インターフェースを継承してAsSpanを公開する.
inlineArrayが使える場合はスマートに書けます.
(※コンストラクタがグロテスクになるのでstatic関数でリストを作成するとよいかも)
public struct Buffer4<T> : IStructBuffer<T> // Tが4つ入るバッファ
{
public T t0, t1, t2, t3;
public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref t0, 4);
public static FixedList<T, Buffer4<T>> List() => new FixedList<T, Buffer4<T>>();
}
public struct Buffer16<T> : IStructBuffer<T> // Tが16個入るバッファ
{
public Buffer4<T> t0, t1, t2, t3;
public Span<T> AsSpan() => MemoryMarshal.CreateSpan(ref t0.t0, 16);
public static FixedList<T, Buffer16<T>> List() => new FixedList<T, Buffer16<T>>();
}
var list4 = Buffer4<int>.List();
list4.Add(1);
list4.Add(2);
list4.Add(1);
list4.Add(1);
list4.RemoveRange(1, 2);
list4.Insert(1, 3);
foreach(var i in list4.AsSpan())
{
Console.WriteLine(i); // 1,3,1
}
コード
public interface IStructBuffer<T>
{
Span<T> AsSpan();
}
public struct FixedList<T,BufferT> where BufferT:IStructBuffer<T>
{
private int count;
private BufferT buffer;
public Span<T> AsSpan() => buffer.AsSpan().Slice(0, count);
public int Count => count;
public bool ContainsIndex(int i) => 0 <= i && i < count;
// Edit Method
public void Clear() => count = 0;
public void Add(in T add)
{
var span = buffer.AsSpan();
if (span.Length < count + 1) throw new IndexOutOfRangeException("確保バッファを超過");
span[count++] = add;
}
public void RemoveRange(int start, int range)
{
if (!ContainsIndex(start)) throw new IndexOutOfRangeException("削除開始インデックスがリスト外");
if (!ContainsIndex(start + range - 1)) throw new IndexOutOfRangeException("削除開始インデックスから指定範囲を行くとはみ出ます");
count -= range;
var span = buffer.AsSpan();
int backCount = span.Length - start - range;
Span<T> copyFrom = span.Slice(start + range, backCount);// 削除範囲含まない後ろの全部
Span<T> copyTo = span.Slice(start, backCount);// 削除開始を含む後ろ
copyFrom.CopyTo(copyTo);
for(int i = 0; i < range; i++)
{
span[count + i] = default;
}
}
public void Insert(int index, in T insert)
{
int add = 1;
int newCount = count + add;
var span = buffer.AsSpan();
int bufferCapacity = span.Length;
if (bufferCapacity < newCount) throw new IndexOutOfRangeException();
count = newCount;
int backCount = bufferCapacity - index - add;
Span<T> copyFrom = span.Slice(index, backCount);
Span<T> copyTo = span.Slice(index + add, backCount);
copyFrom.CopyTo(copyTo);
span[index] = insert;
}
public void Insert(int index, in T t0, in T t1, in T t2)
{
int add = 3;
int newCount = count + add;
var span = buffer.AsSpan();
int bufferCapacity = span.Length;
if (bufferCapacity < newCount) throw new IndexOutOfRangeException();
count = newCount;
int backCount = bufferCapacity - index - add;
Span<T> copyFrom = span.Slice(index, backCount);
Span<T> copyTo = span.Slice(index + add, backCount);
copyFrom.CopyTo(copyTo);
span[index++] = t0;
span[index++] = t1;
span[index] = t2;
}
}