2011-10-07 2 views
6

.NET 4.0 사용 Marshal 클래스를 사용하여 구조체를 바이트 배열로 신속하게 변환 할 수 있습니다. 예를 들어, 다음과 같은 간단한 예 ... 내 목적을 위해 충분히 빨리 내 컴퓨터에서 초당 1 백만 시간에WinRT 및 구조체를 바이트 배열로 유지하고 있습니까?

[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; 
    } 

를 실행하지만 원수 클래스는 충분히 합리적이다 WinRT, 아래에 사용할 수 없습니다 보안상의 이유로,하지만 내 구조체를 바이트 배열로 /에서 달성하는 다른 방법이 필요하다는 것을 의미합니다.

고정 크기 구조체에서 작동하는 접근 방식을 찾고 있습니다. 해당 구조체를 변환하는 방법을 알고 바이트 배열을 형성하는 방법을 알고있는 각 구조체에 대한 사용자 지정 코드를 작성하여 문제를 해결할 수 있습니다.하지만 다소 지루하고 많은 일반적인 해결책이 있다는 것을 느낄 수는 없습니다.

+0

이진 직렬화가 그림에서 벗어 났습니까? 또 다른 질문은 성능이 중요하다면 'AllocHGlobal'을 포함하는 것이 이상하게 보입니다. – user7116

+0

각 호출에 대해 AllocHGlobal을 사용하는 것이 좋습니다. 내 실제 구현은 좀 더 효율적으로 복잡해졌습니다. len, arr 및 ptr을 캐시하여 바이트로 변환하는 각 실제 호출에 Marshal.StructureToPtr 및 Marshal.Copy 만 포함되도록 캐시합니다. 게시 된 코드는 단순화 된 예제였습니다. –

+0

이진 직렬화 문제는 오버 헤드입니다. 단일 int32 필드가있는 구조체는 140 바이트와 같은 것으로 직렬화됩니다. 구조가 상당히 큰 경우이 오버 헤드는 큰 문제가 아니지만 제 시나리오에서는 상대적으로 작은 객체가 많이 있습니다. 따라서 구조체를 실제 4 바이트로 변환하는 것은 필자의 경우 큰 절약이다. –

답변

2

한 가지 방법은 (내가 구현 세부로 캐싱을 떠날거야) 표현식과 반사의 조합이 될 것입니다 :

// 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(); 
} 

은과 같이 사용 :

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(); 
} 

이 다시 읽으려면, 그것을

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

나는 최상의 성능을 내기 위해 표현을 만들고 컴파일하는 접근 방식을 좋아합니다. –

+0

동적으로 생성 된 코드를 생성하기 때문에 Windows 스토어 앱에서 허용되지 않습니다. 맞습니까? – Ani