7

一般的な構造体のサイズを調べる必要があります (sizeof(T) のようにできないか、Marshal.SizeOf(...) 0> を使用するとエラーが発生します)

だから私は書いた:

public static class HelperMethods
{
    static HelperMethods()
    {
        SizeOfType = createSizeOfFunc();
    }

    public static int SizeOf<T>()
    {
        return SizeOfType(typeof(T));
    }

    public static readonly Func<Type, int> SizeOfType = null;

    private static Func<Type, int> createSizeOfFunc()
    {
        var dm = new DynamicMethod("SizeOfType", typeof(int), new Type[] { typeof(Type) });

        ILGenerator il = dm.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Sizeof); //needs to be il.Emit(OpCodes.Sizeof, typeof(something))
        il.Emit(OpCodes.Ret);

        var func = (Func<Type, int>)dm.CreateDelegate(typeof(Func<Type, int>));
        return func;
    }
}

問題は、il.Emit(OpCodes.Sizeof) がメソッド (SizeOfType) の作成中に渡すことができない引数を必要とすることです。IL を使用して、スタックにあるパラメーターを il.Emit(OpCodes.Sizeof) に渡すにはどうすればよいですか? (または別の解決策ですが、2番目の回答で提案されている結果ではなく、関数(デリゲート)をキャッシュしたい)

4

4 に答える 4

9

コンピューティング サイズは、使用しているコンテキストで何が意味を持つかを知る必要があるため、問題をはらんでいます。引数がジェネリック構造体である場合にスローする正当な理由があると思いMarshal.SizeOfますが、それが何であるかはわかりません。

Marshal.SizeOfその警告により、このコードは機能しているように見え、非ジェネリック構造体に対しても同様の結果が得られます。型の sizeof IL オペコードを介してサイズを取得する新しい動的メソッドを生成します。次に、将来の使用のために結果をキャッシュします (動的メソッドの生成にはかなりのコストがかかるため)。

public class A { int x,y,z; }
public struct B { int x,y,z,w,a,b; }
public struct C<T> { Guid g; T b,c,d,e,f; }

public class Program
{
    public static void Main(string[] args)
    {
        Console.WriteLine(IntPtr.Size); // on x86 == 4
        Console.WriteLine(SizeHelper.SizeOf(typeof(C<double>))); // prints 56 on x86
        Console.WriteLine(SizeHelper.SizeOf(typeof(C<int>))); // prints 36 on x86
    }
}

static class SizeHelper
{
    private static Dictionary<Type, int> sizes = new Dictionary<Type, int>();

    public static int SizeOf(Type type)
    {
        int size;
        if (sizes.TryGetValue(type, out size))
        {
            return size;
        }

        size = SizeOfType(type);
        sizes.Add(type, size);
        return size;            
    }

    private static int SizeOfType(Type type)
    {
        var dm = new DynamicMethod("SizeOfType", typeof(int), new Type[] { });
        ILGenerator il = dm.GetILGenerator();
        il.Emit(OpCodes.Sizeof, type);
        il.Emit(OpCodes.Ret);
        return (int)dm.Invoke(null, null);
    }
}

編集

私が知る限り、キャッシュできる非汎用デリゲートを作成する方法はありません。SizeOfオペコードにはメタデータ トークンが必要です。評価スタックから値を取得しません。

実際、以下のコードも同様に機能します。Marshal.SizeOf(Type)型がジェネリック構造であるのにそうでない場合に、引数の例外をスローする理由がわかりMarshal.SizeOf(Object)ません。

    public static int SizeOf<T>() where T : struct
    {
        return Marshal.SizeOf(default(T));
    }
于 2013-08-11T00:22:37.170 に答える
4

コンパイル時にType関数の戻り値の型から引数を解決することが目的のようです。Func<Type, int>この情報はコンパイル時には不明であり、実行時にリフレクションを使用してこの情報を解決する明らかな方法はありません。

動的メソッドを呼び出してすぐに結果を返す代わりに、動的メソッドを返すことの利点がわかりません。あなたがコンテキストを与えていないことを考えると、私は明らかな解決策を提案します。すぐに値を返します。懸念がパフォーマンスにある場合は、結果を辞書にキャッシュするだけです。

public static class GlobalExtensions
{

    public static int SizeOf<T>()
    {
        return SizeOf(typeof (T));
    }

    public static int SizeOf(this Type type)
    {
        var dynamicMethod = new DynamicMethod("SizeOf", typeof(int), Type.EmptyTypes);
        var generator = dynamicMethod.GetILGenerator();

        generator.Emit(OpCodes.Sizeof, type);
        generator.Emit(OpCodes.Ret);

        var function = (Func<int>) dynamicMethod.CreateDelegate(typeof(Func<int>));
        return function();
    }
}

拡張メソッドを使用すると、いくつかの優れた構文が活用されます。次のコードを使用して、一般的な構造体またはクラスのサイズを取得できます。

var size = TypeExtensions.SizeOf<Example<int>>();

//alternative syntax... 
size = typeof (Example<int>).SizeOf();
于 2013-08-11T00:27:40.833 に答える