続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);
    }
}

構造体のフィールド名からオフセットが求まるので、後はポインタ演算で強引に。(^^;