Structure size with general type fields

I want to estimate the size of an array of structures containing type-type parameters, in this case a dictionary entry struct. For this I need the size of the structure.

struct Entry { int hash; int next; TKey key; TValue value; } 

How can I get the size in bytes of this structure?

Edit

Using Marshal.SizeOf seems to be problematic. Passing a structure type will throw an exception, saying that an argument cannot be a general type definition.

If I instead call overload, which takes an instance, for example. Marshal.SizeOf(default(Entry)) it will work if both arguments of a common type are value types. If the general arguments are, for example, <int, object> , then this exception is thrown

Dictionary`2 + Record [System.Int32, System.Object] 'cannot be marshaled as an unmanaged structure; no significant size or bias.

+4
source share
4 answers

It looks like the IL sizeof instruction might be what you need. The sizeof statement is used by the C # sizeof operator behind the scenes, but for some reason, the IL version has fewer restrictions.

The CLI ECMA specification (section III, section 4.25) describes the sizeof statement:

Returns the size in bytes of the type. typeTok can be a general parameter, a reference type, or a value type.

For a reference type, the returned size is the size of the reference value of the corresponding type, and not the size of the data stored in the objects is called the reference value.

[Rationale: the definition of the type of value may vary between the time the CIL was created and the time during which it is loaded for execution. Thus, the type size is not always known when the CIL is generated. The sizeof command allows you to define CIL code to determine the size at runtime without having to call the Framework library class. The calculation can occur completely at runtime or at compile time in CIL-native. sizeof returns the total size that each element in an array of this type will occupy - including any additions you want to add. In particular, the elements of the array are sizeof bytes. final rationale]

You should be able to get the sizeof statement with a little simple runtime code:

 Console.WriteLine("Entry is " + TypeHelper.SizeOf(typeof(Entry)) + " bytes."); // ... public static class TypeHelper { public static int SizeOf<T>(T? obj) where T : struct { if (obj == null) throw new ArgumentNullException("obj"); return SizeOf(typeof(T?)); } public static int SizeOf<T>(T obj) { if (obj == null) throw new ArgumentNullException("obj"); return SizeOf(obj.GetType()); } public static int SizeOf(Type t) { if (t == null) throw new ArgumentNullException("t"); return _cache.GetOrAdd(t, t2 => { var dm = new DynamicMethod("$", typeof(int), Type.EmptyTypes); ILGenerator il = dm.GetILGenerator(); il.Emit(OpCodes.Sizeof, t2); il.Emit(OpCodes.Ret); var func = (Func<int>)dm.CreateDelegate(typeof(Func<int>)); return func(); }); } private static readonly ConcurrentDictionary<Type, int> _cache = new ConcurrentDictionary<Type, int>(); } 
+8
source

The approximate size would be the sum hash (4 bytes (32-bit architecture)) + next (4 bytes (32-bit architecture)) + TKey (if the reference type is 4 bytes for the pointer (32-bit architecture), if the value type is size this type of value calculated in recursion)) + TValue (same as TKey )

or

just using the Marshal.SizeOf method .

+4
source

You can also use Marshal.ReadIntPtr(type.TypeHandle.Value, 4) . It returns the base size of the managed entity instance. See http://msdn.microsoft.com/en-us/magazine/cc163791.aspx for more information on the runtime memory layout.

0
source

(After I wrote this, I noticed that the approach is supposed to be in the rationale quoted by LukeH)

 struct Pin : IDisposable { public GCHandle pinHandle; public Pin(object o) { pinHandle = GCHandle.Alloc(o, GCHandleType.Pinned); } public void Dispose() { pinHandle.Free(); } } static class ElementSize<T> { private static int CalcSize(T[] testarray) { using (Pin p = new Pin(testarray)) return (int)(Marshal.UnsafeAddrOfPinnedArrayElement(testarray, 1).ToInt64() - Marshal.UnsafeAddrOfPinnedArrayElement(testarray, 0).ToInt64()); } static public readonly int Bytes = CalcSize(new T[2]); } 

I'm sure pinning and throwing out a tiny array is cheaper than dynamic compilation. Plus, a static field in a generic class is a great way to have type-safe data like ... no need for a ConcurrentDictionary .

0
source

Source: https://habr.com/ru/post/1480444/


All Articles