Pytanie Wielkość zarządzanych struktur


Platforma .NET 4.0 wprowadza klasy dla odczytywanie i zapisywanie odwzorowanych plików pamięci. Zajęcia koncentrują się wokół metod czytanie i pisanie struktur. Nie są one sortowane, ale kopiowane z pliku i do pliku w formie, w której są układane w zarządzanej pamięci.

Powiedzmy, że chcę zapisać dwie struktury sekwencyjnie do pliku odwzorowanego w pamięci przy użyciu następujących metod:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Foo
{
    public char C;
    public bool B;
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Bar
{
}

static void Write<T1, T2>(T1 item1, T2 item2)
    where T1 : struct
    where T2 : struct
{
    using (MemoryMappedFile file = MemoryMappedFile.CreateNew(null, 32))
    using (MemoryMappedViewAccessor accessor = file.CreateViewAccessor())
    {
        accessor.Write<T1>(0L, ref item1);  //  <-- (1)
        accessor.Write<T2>(??, ref item2);  //  <-- (2)
    }
}

static void Main()
{
    Foo foo = new Foo { C = 'α', B = true };
    Bar bar = new Bar { };
    Write(foo, bar);
}

W jaki sposób uzyskać liczbę bajtów zapisanych w (1), tak aby można napisać następną wartość obok (2)?

Uwaga: Liczba bajtów w przykładzie to 3 (= 2 + 1), a nie 5 (= 1 + 4) zwrócone przez Marshal.SizeOf.

Uwaga 2: sizeof nie może określić rozmiaru ogólnych parametrów typu.


12
2018-01-24 16:13


pochodzenie




Odpowiedzi:


Wydaje się, że nie ma udokumentowanego / publicznego sposobu dostępu do wewnętrznego SizeOfType funkcja używana przez MemoryMappedViewAccessor klasa, więc najbardziej praktycznym sposobem uzyskania rozmiaru tych struktur byłoby użycie takiej refleksji:

static readonly Func<Type, uint> SizeOfType = (Func<Type, uint>)Delegate.CreateDelegate(typeof(Func<Type, uint>), typeof(Marshal).GetMethod("SizeOfType", BindingFlags.NonPublic | BindingFlags.Static));

static void Write<T1, T2>(T1 item1, T2 item2)
    where T1 : struct
    where T2 : struct
{
    using (MemoryMappedFile file = MemoryMappedFile.CreateNew(null, 32))
    using (MemoryMappedViewAccessor accessor = file.CreateViewAccessor())
    {
        accessor.Write(0, ref item1);
        accessor.Write(SizeOfType(typeof(T1)), ref item2);
    }
}

4
2018-01-24 16:45



Właśnie to robię w tej chwili. Miałem nadzieję na czystsze rozwiązanie. Opieranie się na wewnętrznych, nieudokumentowanych metodach nie jest dobrym pomysłem IMO. - dtb
Z mojego doświadczenia wynika, że ​​często jest to wystarczająco dobre (przynajmniej w aplikacjach serwerowych), a ponadto zazwyczaj jest to jedyny sposób na zrobienie tego właśnie. - Pent Ploompuu


Możesz użyć Emit, aby uzyskać dostęp do opcji Sizeof i pominąć ograniczenie kompilatora dotyczące uzyskiwania sizeof (T):

var sizeOfMethod = new DynamicMethod(
    "GetManagedSizeImpl"
,   typeof(uint)
,   null
,   true
);
var genSizeOf = sizeOfMethod.GetILGenerator();
genSizeOf.Emit(OpCodes.Sizeof, typeof(T));
genSizeOf.Emit(OpCodes.Ret);
var sizeOfFunctuion = (Func<uint>)sizeOfMethod
    .CreateDelegate(typeof(Func<uint>));
int size = checked((int)sizeOfFunctuion());

2
2017-11-18 22:11



Большое спасибо за ответ, извините за беспокойство.