2014-12-27 3 views
0

Я сделал некоторые исследования по этой теме и нашел некоторые решения, с помощьюПреобразовать объект класса в байтах и ​​создать объект из байтов

  1. MemoryStream и BinaryFormatter классы
  2. Marshal класса

Но ни один из этих методов не работает для моего класса, потому что у моих классов есть массив.

Вот тестовый класс я работаю с:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] 
public class ByteArrayInClass 
{ 
    private byte _option; 
    private ushort _nElements; 
    private byte[] arrayElements; 

    public ByteArrayInClass(byte option, ushort nElements) 
    { 
     this._option = option; 
     this._nElements = nElements; 
     arrayElements = new byte[nElements]; 
     for (int i = 0; i < arrayElements.Length; i++) 
     { 
      arrayElements[i] = (byte)i; 
     } 
    } 

    public static byte[] ObjectToBytes(byteArrayInClass value) 
    { 
    } 

    public static byteArrayInClass BytesToObject(byte[] bytes) 
    { 
    } 
} 

В моем главном:

testObject1 = new ByteArrayInClass(3, 10); 
byte[] testBytes1 = ByteArrayInClass.ObjectToBytes(testObject1); 

byte[] testBytes2 = { 3, 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 
ByteArrayInClass testObject2 = ByteArrayInClass.BytesToObjectbyte(testBytes2); 

Я начинаю думать, что мне нужно, чтобы преобразовать член класса в байты одного по одному, и наоборот для преобразования байтов в объект. Может ли кто-нибудь указать мне в правильном направлении?

Редактировать: Я не был достаточно ясен о том, что я пытаюсь сделать. Я работаю над программой, которая общается с сервером. Он включает в себя получение данных и отправку данных. Данные отправляются и принимаются в байтах, и когда я получаю байты данных, мне нужно построить класс с полученными байтами, поэтому я понимаю, что отправляется мне. Когда я отправляю данные на сервер, сначала создаю класс с соответствующими значениями, а затем конвертирую объект в байты, поэтому я могу отправить данные на сервер. Надеюсь, это объяснит немного лучше, что я пытаюсь сделать.

Похоже, что нет простого способа преобразования класса в байты, поэтому я сам преобразую каждый класс в байты. Итак, вот что я придумал. Пожалуйста, не стесняйтесь, дайте мне знать, если это глупый способ выполнить задачу. Я хотел бы изучить более умный способ сделать это.

public static int GetObjectSize(ByteArrayInClass value) 
{ 
    return Marshal.SizeOf(value.Option) + Marshal.SizeOf(value.ElementCount) + (value.ElementCount * 1); 
} 

public static byte[] ObjectToBytes(ByteArrayInClass value) 
{ 
    int copyIndex = 0; 
    byte[] resultBytes = new byte[GetObjectSize(value)]; 

    resultBytes[copyIndex] = value.Option; 
    copyIndex += 1; 

    byte[] elementCountBytes = BitConverter.GetBytes(value.ElementCount); 
    elementCountBytes.CopyTo(resultBytes, copyIndex); 
    copyIndex += elementCountBytes.Length; 

    value.ElementArray.CopyTo(resultBytes, copyIndex); 
    return resultBytes; 
} 


public static ByteArrayInClass BytesTObject(byte[] bytes) 
{ 
    int convertIndex = 0; 
    byte option = bytes[convertIndex]; 
    convertIndex += 1; 
    ushort elementCount = BitConverter.ToUInt16(bytes, convertIndex); 
    convertIndex += 2; 

    ByteArrayInClass resultObj = new ByteArrayInClass(option, elementCount); 
    byte[] elementArray = new byte[elementCount]; 
    int j = 0; 
    for (int i = convertIndex; i < (convertIndex + elementCount); i++) 
    { 
     elementArray[j++] = bytes[i]; 
    } 
    resultObj.ElementArray = elementArray; 
    return resultObj; 
} 
+4

Массив байтов не должен быть проблемой. Почему вы используете атрибут «StructLayout» и пытаетесь ли вы сериализовать и деириализовать без него? Обычно такие объекты помечены атрибутом '[Serializable]'.См. Здесь: http://msdn.microsoft.com/en-us/library/4abbf6k0(v=vs.110).aspx –

+0

У меня возникли проблемы с попыткой понять вашу цель, вы пытаетесь преобразовать байты в свой класс и наоборот? –

+0

Код, который вы утверждаете, «ни один из этих методов не работает для моего класса», как-то отсутствует в сообщении. Обязательно добавьте эту часть из кода в образец. Трудно понять, почему код не работает. –

ответ

2

Хорошо это может быть то, что ищете

предположений, почерпнутые из ваших комментариев и обновлений являются

  • Вы хотите общий способ сериализации классы (сообщения)
  • Вы хотите использовать это по проводу (розетки, а не wcf)
  • Он соответствует режиму розетки
  • Вы хотите некоторую базовую защиту от коррупции
  • вы хотите легко использовать

Примечание: там может быть много областей, здесь, которые могут быть улучшены, однако это только, чтобы дать вам некоторые идеи

Если ваш используя что-то вроде веб-сервисов весь этот ответ является недействительным, это действительно только метод adhock сериализации подходит для сокетов

Обзор

Класс Пакета имеет некоторые методы для следующего

  • Способных сериализаций объектов в байты
  • Способной десериализацию байт объектов
  • Можно создавать пакеты с информацией заголовка
  • Заголовок содержит тип сообщения (Опция)
  • Длина всей упаковки
  • Некоторые базовые контрольные суммы и маркеры, в случае коррупции вы можете проверить пакет

Поэтому, учитывая следующее

сообщение Enum

// Some predefined messages 
public enum MessageType : byte 
{ 
    MyClass, 
    MyOtherClass, 
    Message3, 
    Message4, 

} 

Некоторые класс, который вы хотите отправить по проводам

// a serilaizable class 
// Make this as you like 
[Serializable] 
public class MyClass 
{ 

    public byte[] SomeArbitaryBytes { get; set; } 
    public string SomeArbitaryString { get; set; } 
    public int SomeArbitaryInt { get; set; } 
    public double SomeArbitaryDouble { get; set; } 

    public MyClass() 
    { 

     SomeArbitaryString = "hello"; 
     SomeArbitaryInt = 7; 
     SomeArbitaryDouble = 98.1; 
     SomeArbitaryBytes = new byte[10]; 
     for (var i = 0; i < SomeArbitaryBytes.Length; i++) 
     { 
     SomeArbitaryBytes[i] = (byte)i; 
     } 
    } 
} 

Класс Пакет

public static class Package 
{ 
    // Method for basic checksum 
    private static byte GetBasicChecksum(this byte[] data) 
    { 
     byte sum = 0; 
     unchecked // Let overflow occur without exceptions 
     { 
     foreach (byte b in data) 
     { 
      sum += b; 
     } 
     } 
     return sum; 
    } 

    // Serialize to bytes (BinaryFormatter) 
    public static byte[] SerializeToBytes<T>(this T source) 
    { 
     using (var stream = new MemoryStream()) 
     { 
     var formatter = new BinaryFormatter(); 
     formatter.Serialize(stream, source); 
     return stream.ToArray(); 
     } 
    } 

    // Deerialize from bytes (BinaryFormatter) 
    public static T DeserializeFromBytes<T>(this byte[] source) 
    { 
     using (var stream = new MemoryStream(source)) 
     { 
     var formatter = new BinaryFormatter(); 
     stream.Seek(0, SeekOrigin.Begin); 
     return (T)formatter.Deserialize(stream); 
     } 
    } 

    // Check if we have enough data 
    // will throw if it detects a corruption (basic) 
    // return false if there isnt enough data to determine 
    // return true and length of the package if sucessfull 
    public static bool HasValidPackage(this Stream stream, out Int32 messageSize) 
    { 
     messageSize = -1; 

     if (stream.Length - stream.Position < sizeof(byte) * 2 + sizeof(Int32)) 
     return false; 


     var stx = stream.ReadByte(); 

     if (stx != 2) 
     throw new InvalidDataException("Invalid Package : STX Failed"); 

     var packageLength = new byte[sizeof(Int32)]; 
     stream.Read(packageLength, 0, sizeof(Int32)); 
     messageSize = BitConverter.ToInt32(packageLength, 0) - sizeof(byte) * 3; 
     var checkSum = stream.ReadByte(); 

     if (checkSum != packageLength.GetBasicChecksum()) 
     throw new InvalidDataException("Invalid Package : CheckSum Failed"); 

     return stream.Length >= messageSize; 

    } 

    // Pack the message 
    public static byte[] PackMessage<T>(this T source, MessageType messageType) 
    { 
     var buffer = source.SerializeToBytes(); 
     var packageLength = BitConverter.GetBytes(buffer.Length + sizeof(byte) * 3); 
     using (var stream = new MemoryStream()) 
     { 
     stream.WriteByte(2); 
     stream.Write(packageLength, 0, sizeof(Int32)); 
     stream.WriteByte(packageLength.GetBasicChecksum()); 
     stream.WriteByte((byte)messageType); 
     stream.Write(buffer, 0, buffer.Length); 
     stream.WriteByte(3); 
     return stream.ToArray(); 
     } 
    } 

    // Unpack the message 
    public static MessageType UnPackMessage(this Stream stream, Int32 messageSize, out byte[] buffer) 
    { 

     var messageType = (MessageType)stream.ReadByte(); 
     buffer = new byte[messageSize]; 
     stream.Read(buffer, 0, buffer.Length); 

     var etx = stream.ReadByte(); 

     if (etx != 3) 
     throw new InvalidDataException("Invalid Package : ETX Failed"); 

     return messageType; 
    } 

} 

стороне клиента Код

// create your class 
var myClass = new MyClass(); 

// Create a package out of it 
var bytes = myClass.PackMessage(MessageType.MyClass); 

стороне сервера Код

// this is server side 
using (var stream = new MemoryStream(bytes)) 
{ 
    Int32 messageSize; 

    // if we have a valid package do stuff 
    // this loops until there isnt enough data for a package or empty 
    while (stream.HasValidPackage(out messageSize)) 
    { 

     byte[] buffer; 

     switch (stream.UnPackMessage(messageSize, out buffer)) 
     { 
     case MessageType.MyClass: 
      var myClassCopy = buffer.DeserializeFromBytes<MyClass>(); 
      // do stuff with your class 
      break; 
     case MessageType.MyOtherClass: 
      break; 
     case MessageType.Message3: 
      break; 
     case MessageType.Message4: 
      break; 
     default: 
      throw new ArgumentOutOfRangeException(); 
     } 

    } 

    // do something with the remaining bytes here, if any, i.e partial package 

} 
1

C# не C/C++, так что вы не можете просто использовать адресную арифметику, как вы хотите (насколько я вижу).

В .NET правильным способом преобразования в/из массива байтов является сериализация/десериализация.

Или, может быть, вам понадобится BitConverter, если вы хотите эмулировать низкий уровень.

1

Трудно сказать, является ли сериализация конечным результатом или просто методом, используемым для достижения какой-либо другой цели. шаг к какой-то другой цели. Byte массивы сериализации просто отлично, однако:

[Serializable] 
public class ByteArrayClass 
{ 
    public byte[] FirstArray {get; set;} 
    public byte[] SecondArray {get; set;}   
} 

Затем сделать круглый тест поездки:

ByteArrayClass myFoo = new ByteArrayClass(); 
    myFoo.FirstArray = new byte[] { 3, 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 
    myFoo.SecondArray = new byte[] { 3, 11, 5, 1, 21, 23, 4, 5, 4, 7, 8, 9, 10 }; 

    using (FileStream fs = new FileStream(@"C:\Temp\Arry.Bin", 
          FileMode.Create, FileAccess.Write)) 
    { 
     BinaryFormatter bf = new BinaryFormatter(); 
     bf.Serialize(fs, myFoo); 
    } 

    ByteArrayInClass newFoo; 
    using (FileStream fs = new FileStream(@"C:\Temp\Arry.Bin", 
         FileMode.Open, FileAccess.Read)) 
    { 
     BinaryFormatter bf = new BinaryFormatter(); 
     newFoo = (ByteArrayClass) bf.Deserialize(fs); 
    } 

Я бы упущением, если бы я не добавил, что ProtoBuf-Net гораздо быстрее, лучше сериалайзер чем BinaryFormatter , Он также лучше подходит для передачи данных - класс, сериализованный одной сборкой, может быть десериализован другим, и он производит меньший результат.

1

Его трудно сказать, что вы на самом деле хотите достичь, однако плохо дать ему выстрелили

Это рядный с намеченной цели?

public class ByteArrayInClass 
{ 
    public byte Option { get; set; } 
    public ushort Elements { get; set; } 
    public byte[] Bytes { get; set; } 

    public ByteArrayInClass(byte option, ushort nElements) 
    { 
     this.Option = option; 
     this.Elements = nElements; 
     this.Bytes = new byte[nElements]; 
     for (var i = 0; i < nElements; i++) 
     { 
     this.Bytes[i] = (byte)i; 
     } 
    } 
    public ByteArrayInClass(byte[] array) 
    { 
     this.Elements = (ushort)array.Length; 
     this.Bytes = new byte[this.Elements]; 
     array.CopyTo(this.Bytes, 0); 
    } 
    public static byte[] ObjectToBytes(ByteArrayInClass value) 
    { 
     var result = new byte[value.Elements]; 
     value.Bytes.CopyTo(result, 0); 
     return result; 
    } 

    public static ByteArrayInClass BytesToObject(byte[] bytes) 
    { 
     return new ByteArrayInClass(bytes); 
    } 
} 
+0

Я думаю, что это близко к тому, что я Когда я преобразовываю класс в байты, мне нужно преобразовать Opition, Elements и Bytes все в байты. Поэтому я считаю, что единственный способ сделать это - преобразовать каждый член класса в байты и вернуть все байты в виде результирующих байтов. –

+0

Да, это может быть сделано, и, как вы говорите, это можно сделать вручную, однако это также можно сделать автоматически, если вы хотите, чтобы это было правильно, так как у меня было много классов с большим количеством членов класса. сериализации, есть ли причина, по которой вы не хотите использовать сериализацию ?, если бы ее размер мог вероятно, даст вам пример использования proto-buf, который очень быстр и эффективен –

+0

Я хотел бы научиться делать это с помощью сериализации вместо преобразования каждого члена класса вручную. Пожалуйста, покажите пример без использования proto-buf, я пытаюсь использовать только платформу .NET без установки сторонних компонентов. Образец объекта, который я создал, имеет 13 байт, но когда я пытаюсь использовать метод сериализации, описанный Plutonix, я получаю MemoryStream с размером 208 по некоторым причинам. –

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