2011-10-07 13 views
6

Korzystanie .NET 4.0 Potrafię szybko przekonwertować strukturę do tablicy bajtów i z nich, korzystając z klasy Marshal. Na przykład, następujący prosty przykład będzie działał na około 1 milion razy na sekundę na moim komputerze, który jest wystarczająco szybki dla moich celów ...WinRT i trwająca struktura do iz tablicy bajtów?

[StructLayout(LayoutKind.Sequential)] 
    public struct ExampleStruct 
    { 
     int i1; 
     int i2; 
    } 

    public byte[] StructToBytes() 
    { 
     ExampleStruct inst = new ExampleStruct(); 

     int len = Marshal.SizeOf(inst); 
     byte[] arr = new byte[len]; 
     IntPtr ptr = Marshal.AllocHGlobal(len); 
     Marshal.StructureToPtr(inst, ptr, true); 
     Marshal.Copy(ptr, arr, 0, len); 
     Marshal.FreeHGlobal(ptr); 

     return arr; 
    } 

Ale klasa Marszałek nie jest dostępny pod WinRT, który jest na tyle rozsądny ze względów bezpieczeństwa, ale oznacza to, że potrzebuję innego sposobu na uzyskanie mojej tablicy struct/byte from array.

Szukam podejścia, które działa dla każdej stałej wielkości struct. Mógłbym rozwiązać problem, pisząc niestandardowy kod dla każdej struktury, która wie, jak przekonwertować tę konkretną strukturę i utworzyć tablicę bajtów, ale jest to raczej uciążliwe i nie mogę oprzeć się wrażeniu, że istnieje pewne ogólne rozwiązanie.

+0

Czy serializacja binarna jest niedostępna? Kolejne pytanie brzmi: jeśli wydajność jest ważna, dziwnym wydaje się być użycie "AllocHGlobal". – user7116

+0

Dobra uwaga dotycząca używania AllocHGlobal dla każdego połączenia. Moje faktyczne wdrożenie jest nieco bardziej skomplikowane, aby być bardziej wydajnym. Przechowuje pliki len, arr i ptr, tak aby każde faktyczne wywołanie konwersji na bajty dotyczyło tylko Marshal.StructureToPtr i Marshal.Copy. Zamieszczony kod był tylko uproszczonym przykładem. –

+0

Problem z serializacją binarną jest związany z obciążeniem. Struktura z pojedynczym polem int32 zostanie zserializowana do około 140 bajtów. Jeśli twoje struktury są dość duże, to obciążenie to nie jest wielka sprawa, ale w moim scenariuszu mam wiele obiektów, które są względnie małe. Zatem przekonwertowanie struktury do rzeczywistych 4 bajtów, które zawiera, to duża oszczędność w moim przypadku. –

Odpowiedz

2

Jedno podejście byłoby połączenie ekspresji i refleksji (Zostawię buforowanie jako szczegółach implementacji):

// Action for a given struct that writes each field to a BinaryWriter 
static Action<BinaryWriter, T> CreateWriter<T>() 
{ 
    // TODO: cache/validate T is a "simple" struct 

    var bw = Expression.Parameter(typeof(BinaryWriter), "bw"); 
    var obj = Expression.Parameter(typeof(T), "value"); 

    // I could not determine if .Net for Metro had BlockExpression or not 
    // and if it does not you'll need a shim that returns a dummy value 
    // to compose with addition or boolean operations 
    var body = Expression.Block(
     from f in typeof(T).GetTypeInfo().DeclaredFields 
     select Expression.Call(
      bw, 
      "Write", 
      Type.EmptyTypes, // Not a generic method 
      new[] { Expression.Field(obj, f.Name) })); 

    var action = Expression.Lambda<Action<BinaryWriter, T>>(
     body, 
     new[] { bw, obj }); 

    return action.Compile(); 
} 

Używane tak:

public static byte[] GetBytes<T>(T value) 
{ 
    // TODO: validation and caching as necessary 
    var writer = CreateWriter(value); 
    var memory = new MemoryStream(); 
    writer(new BinaryWriter(memory), value); 
    return memory.ToArray(); 
} 

Aby zapoznać się wstecz, jest nieco bardziej zaangażowany:

static MethodInfo[] readers = typeof(BinaryReader).GetTypeInfo() 
    .DeclaredMethods 
    .Where(m => m.Name.StartsWith("Read") && !m.GetParameters().Any()) 
    .ToArray(); 

// Action for a given struct that reads each field from a BinaryReader 
static Func<BinaryReader, T> CreateReader<T>() 
{ 
    // TODO: cache/validate T is a "simple" struct 

    var br = Expression.Parameter(typeof(BinaryReader), "br"); 

    var info = typeof(T).GetTypeInfo(); 

    var body = Expression.MemberInit(
     Expression.New(typeof(T)), 
     from f in info.DeclaredFields 
     select Expression.Bind(
      f, 
      Expression.Call(
       br, 
       readers.Single(m => m.ReturnType == f.FieldType), 
       Type.EmptyTypes, // Not a generic method 
       new Expression[0])); 

    var function = Expression.Lambda<Func<BinaryReader, T>>(
     body, 
     new[] { br }); 

    return function.Compile(); 
} 
+0

Podoba mi się podejście polegające na budowaniu ekspresji i kompilowaniu jej w celu uzyskania najlepszej wydajności. –

+0

Nie sądzę, że będzie to dozwolone w aplikacji Windows Store, ponieważ dynamicznie generuje kod do wykonania, prawda? – Ani

Powiązane problemy