객체를 바이트로 변환 []


138

프로토 타입 TCP 연결을 작성 중이며 전송할 데이터를 균질화하는 데 문제가 있습니다.

현재는 문자열 만 보내지 않지만 앞으로는 개체를 보낼 수 있기를 원합니다.

모든 것이 바이트 배열로 캐스팅 될 수 있다고 생각했기 때문에 코드는 현재 매우 간단합니다.

void SendData(object headerObject, object bodyObject)
{
  byte[] header = (byte[])headerObject;  //strings at runtime, 
  byte[] body = (byte[])bodyObject;      //invalid cast exception

  // Unable to cast object of type 'System.String' to type 'System.Byte[]'.
  ...
}

이것은 물론 쉽게 해결됩니다

if( state.headerObject is System.String ){...}

문제는 내가 그렇게하면 런타임에 byte []로 캐스팅 할 수없는 모든 유형의 객체를 확인해야한다는 것입니다.

런타임에 byte []로 캐스팅 할 수없는 모든 객체를 알지 못하기 때문에 이것은 실제로 옵션이 아닙니다.

C # .NET 4.0에서 객체를 바이트 배열로 어떻게 변환합니까?


2
일반적으로 의미있는 방식으로는 불가능합니다 (예를 들어, 인스턴스 FileStream또는 이와 같은 핸들을 캡슐화하는 객체를 고려하십시오).
Jason

2
모든 클라이언트가 .NET을 실행하도록 하시겠습니까? 대답이 '아니요'인 경우 다른 형식의 직렬화 (XML, JSON 등)를 고려해야합니다.
R. Martinho Fernandes

답변:


195

사용하십시오 BinaryFormatter:

byte[] ObjectToByteArray(object obj)
{
    if(obj == null)
        return null;
    BinaryFormatter bf = new BinaryFormatter();
    using (MemoryStream ms = new MemoryStream())
    {
        bf.Serialize(ms, obj);
        return ms.ToArray();
    }
}

참고 obj및 특성 / 내 필드 obj(그래서-에 해당 속성 / 모든 필드에 대한) 모든 필요가 태그 될 것이다 Serializable속성을 성공적으로 직렬화한다.


13
더 이상 이해가되지 않을 수 있으므로 반대편에있는 "any"객체로 수행하는 작업에주의하십시오 (예 : 해당 객체가 파일에 대한 핸들이거나 이와 유사한 경우)
Rowland Shaw

1
그렇습니다. 일반적인 경고가 적용되지만 사람들에게 상기시키는 것은 나쁜 생각이 아닙니다.
Daniel DiPaolo

24
사용 using된 내부 버퍼를 간절히 릴리스하므로 MemoryStream 사용을 블록 으로 감싸는 것이 좋습니다 .
R. Martinho Fernandes

1
이 방법은 .NET입니까? StructLayoutAtrribute를 사용하여 C 구조체를 직렬화하고 소켓을 통해 C 코드로 보내고 C 코드가 구조체를 이해한다고 기대할 수 있습니까? 아닌 것 같아요?
joe

103

이 기사를 확인하십시오 : http://www.morgantechspace.com/2013/08/convert-object-to-byte-array-and-vice.html

아래 코드를 사용하십시오

// Convert an object to a byte array
private byte[] ObjectToByteArray(Object obj)
{
    if(obj == null)
        return null;

    BinaryFormatter bf = new BinaryFormatter();
    MemoryStream ms = new MemoryStream();
    bf.Serialize(ms, obj);

    return ms.ToArray();
}

// Convert a byte array to an Object
private Object ByteArrayToObject(byte[] arrBytes)
{
    MemoryStream memStream = new MemoryStream();
    BinaryFormatter binForm = new BinaryFormatter();
    memStream.Write(arrBytes, 0, arrBytes.Length);
    memStream.Seek(0, SeekOrigin.Begin);
    Object obj = (Object) binForm.Deserialize(memStream);

    return obj;
}

10
에 대한 코멘트에서 언급 한 바와 같이 이 답변 의는 MemorySteamA의 포장되어야한다 using블록.
rookie1024

내가 adition에서 존중해야 할 것이 있습니까? 그런 식으로 구현하고 3 개의 int32 공용 멤버를 포함하는 객체를 포맷하면 244 바이트 길이의 ByteArray가 생성됩니다. C # 구문에 대해 알지 못하거나 사용하지 못했을 것입니까?
dhein

죄송합니다. 문제가 없습니다. 코드를 게시 할 수 있습니까?
kombsh

@kombsh 짧은 형식으로 시도합니다 : [Serializable] class GameConfiguration {public map_options_t enumMapIndex; 공개 Int32 iPlayerAmount; 개인 Int32 iGameID; } 바이트 [] baPacket; GameConfiguration objGameConfClient = 새로운 GameConfiguration (); baPacket = BinModler.ObjectToByteArray (objGameConfClient); 이제 baPacket에는 약 244 바이트의 내용이 들어 있습니다. 나는 12을 기대했다.
dhein

1
@kombsh 예제에서 일회용 개체를 명시 적으로 처리 할 수 ​​있습니다.
Rudolf Dvoracek

30

다른 사람들이 이전에 말했듯이 이진 직렬화를 사용할 수는 있지만 여분의 바이트를 생성하거나 정확히 동일한 데이터가 아닌 객체로 직렬화 해제 할 수 있습니다. 반면에 반사를 사용하는 것은 매우 복잡하고 매우 느립니다. 객체를 바이트로 변환하고 마샬링하는 다른 솔루션이 있습니다.

var size = Marshal.SizeOf(your_object);
// Both managed and unmanaged buffers required.
var bytes = new byte[size];
var ptr = Marshal.AllocHGlobal(size);
// Copy object byte-to-byte to unmanaged memory.
Marshal.StructureToPtr(your_object, ptr, false);
// Copy data from unmanaged memory to managed buffer.
Marshal.Copy(ptr, bytes, 0, size);
// Release unmanaged memory.
Marshal.FreeHGlobal(ptr);

그리고 바이트를 객체로 변환하려면 :

var bytes = new byte[size];
var ptr = Marshal.AllocHGlobal(size);
Marshal.Copy(bytes, 0, ptr, size);
var your_object = (YourType)Marshal.PtrToStructure(ptr, typeof(YourType));
Marshal.FreeHGlobal(ptr);

작은 객체에 대해이 접근 방식을 사용하는 것은 눈에 띄게 느리고 부분적으로 안전하지 않으며 (관리되지 않은 메모리에서 이중 복사로 인해 필드별로 자신의 직렬화와 비교하여) 직렬화를 구현하지 않고 객체를 바이트 []로 엄격하게 변환하는 가장 쉬운 방법입니다. 그리고 [직렬화 가능] 속성이 없습니다.


1
StructureToPtr+ Copy가 느리다고 생각 합니까? 직렬화보다 속도가 느릴 수있는 방법은 무엇입니까? 더 빠른 해결책이 있습니까?
Anton Samsonov

몇 가지 간단한 유형으로 구성된 작은 구조체에 사용하면 예 (아주 일반적인 경우) 마샬링 및 쿼드 복사 (객체에서 힙으로, 힙에서 바이트로, 바이트에서 힙으로, 힙에서) 반대하다). 바이트 대신 IntPtr을 사용하면 더 빠를 수 있지만이 경우에는 그렇지 않습니다. 이러한 유형의 경우 바이트 배열에 값을 넣는 자체 직렬 변환기를 작성하는 것이 더 빠릅니다. 내장 직렬화보다 속도가 느리거나 "매우 느리다"고 말하는 것은 아닙니다.
Aberro

1
바이트 단위로 매핑 되므로이 방법이 마음에 듭니다. 이것은 C ++ 매핑과 메모리를 교환하는 정말 좋은 방법입니다. 당신을 위해 +1.
Hao Nguyen

2
잠재적 사용자에게는 매우 똑똑하지만이 답변은 구조 배열, 관리되지 않는 구조로 마샬링 할 수없는 개체 또는 계층 구조에 ComVisible (false) 부모가있는 개체에서는 작동하지 않습니다.
TernaryTopiary

1
"크기"를 어떻게 얻었는지 deserilize? 의var bytes = new byte[size];
리카르도

13

당신이 찾고있는 것은 직렬화입니다. .Net 플랫폼에 사용 가능한 몇 가지 직렬화 형식이 있습니다.


10
public static class SerializerDeserializerExtensions
{
    public static byte[] Serializer(this object _object)
    {   
        byte[] bytes;
        using (var _MemoryStream = new MemoryStream())
        {
            IFormatter _BinaryFormatter = new BinaryFormatter();
            _BinaryFormatter.Serialize(_MemoryStream, _object);
            bytes = _MemoryStream.ToArray();
        }
        return bytes;
    }

    public static T Deserializer<T>(this byte[] _byteArray)
    {   
        T ReturnValue;
        using (var _MemoryStream = new MemoryStream(_byteArray))
        {
            IFormatter _BinaryFormatter = new BinaryFormatter();
            ReturnValue = (T)_BinaryFormatter.Deserialize(_MemoryStream);    
        }
        return ReturnValue;
    }
}

아래 코드와 같이 사용할 수 있습니다.

DataTable _DataTable = new DataTable();
_DataTable.Columns.Add(new DataColumn("Col1"));
_DataTable.Columns.Add(new DataColumn("Col2"));
_DataTable.Columns.Add(new DataColumn("Col3"));

for (int i = 0; i < 10; i++) {
    DataRow _DataRow = _DataTable.NewRow();
    _DataRow["Col1"] = (i + 1) + "Column 1";
    _DataRow["Col2"] = (i + 1) + "Column 2";
    _DataRow["Col3"] = (i + 1) + "Column 3";
    _DataTable.Rows.Add(_DataRow);
}

byte[] ByteArrayTest =  _DataTable.Serializer();
DataTable dt = ByteArrayTest.Deserializer<DataTable>();

6

을 사용하는 Encoding.UTF8.GetBytes것보다을 사용하는 것이 더 빠릅니다 MemoryStream. 여기서는 NewtonsoftJson 을 사용하여 입력 객체를 JSON 문자열로 변환 한 다음 JSON 문자열에서 바이트를 가져옵니다.

byte[] SerializeObject(object value) =>Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(value));

이 버전의 @Daniel DiPaolo 버전 벤치 마크

Method                    |     Mean |     Error |    StdDev |   Median |  Gen 0 | Allocated |
--------------------------|----------|-----------|-----------|----------|--------|-----------| 
ObjectToByteArray         | 4.983 us | 0.1183 us | 0.2622 us | 4.887 us | 0.9460 |    3.9 KB |
ObjectToByteArrayWithJson | 1.548 us | 0.0309 us | 0.0690 us | 1.528 us | 0.3090 |   1.27 KB |

2

확장 클래스의 결합 솔루션 :

public static class Extensions {

    public static byte[] ToByteArray(this object obj) {
        var size = Marshal.SizeOf(data);
        var bytes = new byte[size];
        var ptr = Marshal.AllocHGlobal(size);
        Marshal.StructureToPtr(data, ptr, false);
        Marshal.Copy(ptr, bytes, 0, size);
        Marshal.FreeHGlobal(ptr);
        return bytes;
   }

    public static string Serialize(this object obj) {
        return JsonConvert.SerializeObject(obj);
   }

}

1

당신이 사용할 수있는 내장 직렬화 도구를 A와 프레임 워크 및 직렬화에 MemoryStream을 . 가장 간단한 옵션이지만 시나리오에 꼭 필요한 것보다 더 큰 바이트 []를 생성 할 수 있습니다.

이 경우 리플렉션을 사용하여 직렬화 할 객체의 필드 및 / 또는 속성을 반복하고 MemoryStream에 수동으로 기록하여 사소한 유형을 직렬화하는 데 필요한 경우 직렬화를 재귀 적으로 호출 할 수 있습니다. 이 방법은 더 복잡하고 구현하는 데 더 많은 시간이 걸리지 만 직렬화 된 스트림을 훨씬 더 많이 제어 할 수 있습니다.


1

이런 간단한 것은 어떻습니까?

return ((object[])value).Cast<byte>().ToArray(); 

1

"캐스팅 바이트"보다 "직렬화"라는 표현을 사용하고 싶습니다. 객체를 직렬화한다는 것은 객체를 원격 상자에서 사용하여 객체를 재구성 할 수있는 바이트 배열 (또는 XML 등)로 변환하는 것을 의미합니다. .NET에서 Serializable속성 은 객체를 직렬화 할 수있는 유형을 표시합니다.


1

객체를 바이트 배열로 변환하는 다른 방법 :

TypeConverter objConverter = TypeDescriptor.GetConverter(objMsg.GetType());
byte[] data = (byte[])objConverter.ConvertTo(objMsg, typeof(byte[]));

이것을 시험해 보니 .NET 4.6.1 및 Windows 10에서 저에게 적합하지 않은 것 같습니다.
Contango

0

Newtonsoft.Json 바이너리 JSON 을 사용 하고 [Serializable] 속성으로 모든 것을 표시 할 필요가없는 추가 구현 입니다. 한 가지 단점은 객체를 익명 클래스로 래핑해야한다는 것이므로 이진 직렬화로 얻은 바이트 배열은이 클래스와 다를 수 있습니다.

public static byte[] ConvertToBytes(object obj)
{
    using (var ms = new MemoryStream())
    {
        using (var writer = new BsonWriter(ms))
        {
            var serializer = new JsonSerializer();
            serializer.Serialize(writer, new { Value = obj });
            return ms.ToArray();
        }
    }
}

BSON은 클래스 또는 배열로 시작해야하므로 익명 클래스가 사용됩니다. byte []를 다시 객체로 직렬화 해제하려고 시도하지 않았으며 작동하는지 확실하지 않지만 byte [] 로의 변환 속도를 테스트했으며 내 요구를 완전히 충족시킵니다.


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.