RefList`1
というわけでさらさら書いてみます。
.field priate !T&[] store .property instance !T& Item(int32) { .get instance !T& RefList`1::get_Item(int32) .set instnace void RefList`1::set_Item(int32, !T&) } .method public hidebysig specialname instance !T& get_Item(int32 index) cil managed { .maxstack 2 ldarg.0 ldfld !0[] class RefList`1<!T>::store ldarg.1 ldelem !T& stloc.0 ret } .method public hidebysig specialname instance void set_Item(int32 index, !T& value) cil managed { .maxstack 8 ldarg.0 ldfld !0[] class RefList`1<!T>::store ldarg.1 ldarg.2 stelem !T& ret }
これに RefList() : this(10) と RefList(int capacity) のコンストラクタだけ作成してコンパイル。ここまではさくっと通りました。
ここまで終わったところで
FFのメンテ明けのためArgus張りにいってます(笑
Reflector.NET で表示してみる
public class RefList<T> where struct { private ref T[] store; public RefList() : this(10) {} public RefList(int capacity) { this.store = new ref T[capacity]; } public ref T this[int index] { get { return this.store[index]; } set { this.store[index] = value; } } }
なかなか期待通りのディスコンパイル結果をだしてくれた。使ってみよう。
public static class Program { static void Main() { RefList<Rectangle> list = new RefList<Rectangle>; Rectangle r = new Rectangle(); list[0] = new Rectangle(); // 代入できるか? list[0].Offset(10, 10); // メソッド呼べるか? Console.WriteLine(list[0]); // ref Rectangle がどう表示されるか? } }
結果は、下3行が、「CS1501: 引数を1個指定できるメソッド get_Item() のオーバーロードはありません。」となりました。やっぱり無理みたいです。代入文のほうは、
list.set_Item(0, ref r);
とするとコンパイルエラーは消えましたが、取得側は、
list.get_Item(0)
としても CS1501 のままで、ちょっと残念なかんじでした。
もうちょっと遊んでみます。
Argus わかないし(笑
public void Dump() { for (int num1 = 0; num1 < this.store.Length; num1++) { Console.WriteLine(this.store[num1]); } }
という具合にディスアセンブルされるような IL を記述してみました。テストコードは、
static void Main() { RefList<int> list = new RefList<int>(2); int n = 1; int m = 2; list.set_Item(0, ref n); list.set_Item(0, ref m); list.Dump(); n = 5; m = 10; list.Dump(); }
こうです。期待する結果は格納されている値だけ頑張ってくれて
1 2 5 10
なのですが、
ハンドルされていない例外: COMException(略) ByRef 型のフィールドです。(略) HRESULT:0x801312E4
場所 Program.main()
Dump() の最後は、Console.WriteLine(object) を呼び出していたため、マネージドポインタは渡せないよ!って実行時例外になっちゃたのかな? そもそも where struct してあるので、
ldelem !T& call void System.Console::WriteLine(object) ↓ ldelem !T& ldobj !T box !T call void System.Console::WriteLine(object)
と修正しまみました。Reflector.NET のディスアセンブル結果が、
for (int num1 = 0; num1 < this.store.Length; num1++) { Console.WriteLine(*(this.store[num1])); }
と、unsafe っぽくなりましたが、これでも同様の例外が出ます。例外文をよくみると、「ByRef 型のフィールド」と書いてあるので、どうも ref T[] store というフィールドが作れないってエラーっぽいですね。*1
ということは、ref T 型ではなくて T 型の配列を保持しておき、get で ref T を返すことで中身を直接変更できるインターフェスにすれば、この問題は解決できるかもしれません。
*1:Main() の .init あたりで落ちてるぽいので、そもそも例外が発生しているのはどうやら Generic クラスの具体型の生成時っぽいです。