続Genericsとunsafe
某所より。
だもんで、いま、T array, int indexを管理するPointerという
クラスをgenericsで書いて、C++的なポインタ演算や、
S : structへの強制castが出来ると便利なのに、と思ったんですが(´ω`)
そのままズバリなクラスは作成出来ませんでしたが、こんなのはどーでしょうか?
using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Diagnostics; public struct Point { public int X; public int Y; public int Z; } // Tを構造体Sの配列としてアクセスさせるクラス class Pointer<T,S> : IDisposable where T : struct where S : struct { public GCHandle handle; public IntPtr offset; public int size; public Pointer(T data, params string fields) { handle = GCHandle.Alloc(data, GCHandleType.Pinned); // 構造体のフィールドのオフセットを取得 offset = new IntPtr[fields.Length]; for (int i = 0; i < fields.Length; ++i) offset[i] = Marshal.OffsetOf(typeof(S), fields[i]); // 構造体のサイズを取得 size = Marshal.SizeOf(typeof(S)); } public Int32 GetInt32(int index, int field) { unsafe { byte* p = (byte*)Marshal.UnsafeAddrOfPinnedArrayElement((Array)handle.Target, 0); return *(Int32*)(p + index * size + (int)offset[field]); } } public Int16 GetInt16(int index, int field) { unsafe { byte* p = (byte*)Marshal.UnsafeAddrOfPinnedArrayElement((Array)handle.Target, 0); return *(Int16*)(p + index * size + (int)offset[field]); } } public Byte GetByte(int index, int field) { unsafe { byte* p = (byte*)Marshal.UnsafeAddrOfPinnedArrayElement((Array)handle.Target, 0); return *(p + index * size + (int)offset[field]); } } #region IDisposable メンバ public void Dispose() { handle.Free(); } #endregion } class Program { const int LOOP = 10000000; static void Main(string args) { // byteをPoint構造体として値を設定する byte[] bytes = new byte[4096]; unsafe { fixed (byte* p = &bytes[0]) { Point* pt = (Point*)p; for (int i = 0; i < bytes.Length / sizeof(Point); ++i) { pt[i].X = i; pt[i].Y = i * 2; pt[i].Z = i * 3; } } } // フィールドX,Y,Zをfield=0,1,2でアクセスする Pointer<byte, Point> ptr = new Pointer<byte, Point>(bytes, "X", "Y", "Z"); Stopwatch sw = new Stopwatch(); sw.Start(); for (int i = 0; i < LOOP; ++i) { // bytesをPointの配列に見立てて // 0要素目のフィールド2(Z)をInt32として取得 ptr.GetInt32(0, 2); } ptr.Dispose(); sw.Stop(); Console.WriteLine(sw.ElapsedMilliseconds); } }
構造体のフィールド名からオフセットが求まるので、後はポインタ演算で強引に。(^^;