2013-08-10 4 views
3

мне нужно узнать размер общей структуры (я не могу это сделать, как 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; 
    } 
} 

diffuclty что il.Emit (OpCodes.Sizeof) нужен аргумент, который я не могу передать его во время метода (SizeOfType) создания. Как передать параметр, который находится в стеке для il.Emit (OpCodes.Sizeof), используя IL? (или другое решение, но я хочу кэшировать функцию (делегат), а не результат, который предлагается во 2-м ответе)

+0

Можете ли ваши вопросы быть примером [XY-Problem] (http://www.perlmonks.org/?node_id=542341)? (это и http: // stackoverflow.com/questions/18166429/cast-pointer-to-generic-structure) – I4V

+0

Я заявил, что хочу узнать размер общей структуры (мне нужно выделить неуправляемую память такого размера). Проблема в том, что эти структуры могут быть общепринятым, поэтому известные методы, такие как Marshal.SizeOf (...) и sizeof(), не работают. – dajuric

ответ

4

Размер вычислений - это то, что чревато проблемами, потому что вам нужно знать, что имеет смысл в контекст, который вы используете. Я бы предположил, что есть хорошая причина для 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)); 
    } 
+0

Если программа создаст много больших, но недолговечных структур данных, могут быть существенные преимущества для их разбиения на массивы размером не более 85 000 байт. Необходимое в принципе средство поиска размера массива из N элементов; это значение хорошо определено для всех типов .NET, хотя ни C#, ни VB.NET не предоставляют удобный способ его получения. – supercat

+0

@supercat Я предполагаю, что вы пытаетесь избежать кучи больших объектов. Меня бы интересовал пример, где это работает. Я подозреваю, что разработчики времени исполнения хотят сохранить этот номер в деталях реализации. Кроме того, есть особые случаи. Например, [массивы двойников] (http://connect.microsoft.com/VisualStudio/feedback/details/266330/large-object-heap-loh-does-not-behave-as-expected-for-double-array -замещение) заканчиваются на LOH при температуре свыше 1000 элементов. –

+0

Спасибо. Но мой первоначальный вопрос: можно ли кэшировать метод (созданный делегат) вместо результата? – dajuric

2

Казалось бы, что ваша цель состоит в том, чтобы разрешить спор 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(); 
+0

Спасибо. Я сделал это (первая попытка). Но не будет ли быстрее, если я создам метод делегата, а затем буду использовать его вместо создания метода каждый раз? – dajuric

+0

@ dajuric Ну, самый оптимальный способ сделать это - кешировать ваши результаты. Это звучит сложно, но на самом деле все, что вам нужно сделать, это определить словарь

+0

Спасибо, но нет необходимости. – dajuric

0

Принимая выше мышление на один шаг дальше, я прибыл в:

public static class TypeSize<T> 
{ 
    public readonly static int Size; 

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

... который я считают, что это наиболее эффективное решение проблемы.

Смежные вопросы