Determinación del tamaño serializado de un tipo .NET y la eficiencia de la memoria no administrada

Mi pregunta es si es posible determinar el tamaño serializado (en bytes) de un tipo de referencia.

Heres la situación:

Estoy utilizando la clase BinaryFormatter para serializar tipos básicos de .NET, es decir, por ejemplo:

[Serializable] public class Foo { public string Foo1 { get; set; } public string Foo2 { get; set; } } 

Estoy serializando cada elemento en un byte [], luego agrego ese segmento al final de un byte existente [] y adicionalmente agrego un retorno de carro al final de cada segmento para delimitar los objetos.

Para deserializar utilizo Marshal.ReadByte () de la siguiente manera:

 List buffer = new List(); for (int i = 0; i < MapSize; i++) { byte b = Marshal.ReadByte(readPtr , i); if (b != delim) // read until encounter a carriage return buffer.Add(b); else break; } readPtr = readPtr + buffer.Count + 1; // incrementing the pointer for the next object return buffer.ToArray(); 

Creo que usar Marshal.Copy () sería más eficiente, pero necesito saber la longitud del segmento de bytes serializados de antemano. ¿Hay alguna forma en que pueda calcular esto de manera confiable a partir del tipo que se está serializando, o un método general más eficiente que puedo usar?

Además, el uso de un retorno de carro no será confiable, en última instancia. Entonces, me pregunto si hay una forma más estándar de delimitar los objetos, ya sea a través de la personalización de mi BinaryFormatter o utilizando alguna otra práctica recomendada estandarizada. Por ejemplo, ¿hay una forma específica en la que BinaryFormatter delimita objetos si su serialización, por ejemplo, una lista genérica ?

No hay una forma terriblemente buena de determinar la longitud serializada de antemano. La especificación para el protocolo BinaryFormatter está disponible aquí: http://msdn.microsoft.com/en-us/library/cc236844(v=prot.10).aspx

Te ahorraré la molestia de leerlo para tus propósitos:

  1. Está construido para ser un formato extensible. Esto le permite agregar campos más tarde y aún mantener cierta compatibilidad con implementaciones anteriores. Para sus propósitos, esto significa que la longitud de la forma serializada no es fija en el tiempo.
  2. Es extremadamente frágil. El formato binario en realidad codifica los nombres de los campos en él. Si alguna vez cambia el nombre de un campo, la longitud del formulario serializado cambiará.
  3. El formato binario en realidad abarca una relación de muchos a uno entre las codificaciones serializadas y los datos de objetos. El mismo objeto podría potencialmente codificarse de varias maneras diferentes, con una cantidad de bytes diferentes para la salida (no entenderé por qué está escrito de esa manera).

Si desea una manera fácil de hacer las cosas, simplemente cree una matriz que contenga todos los objetos y serialice esa única matriz. Esto resuelve la mayoría de tus problemas. Todos los problemas de delimitación de los diferentes objetos son manejados por BinaryFormatter. No tendrás una copia de memoria excesiva. La salida final será más compacta porque BinaryFormatter solo tiene que especificar los nombres de los campos una vez por invocación.

Finalmente, puedo decirle que la copia de memoria adicional no es la fuente principal de ineficiencia en su implementación actual. Está obteniendo mucha más ineficiencia con el uso de la reflexión por parte de Binary y el hecho de que codifica los nombres de campo en la salida serializada.

Si la eficiencia es primordial, sugeriría escribir algún código personalizado que codifique el contenido de sus estructuras en formato de “datos antiguos”. Entonces tendrás control sobre cuánto se escribe y cómo.

Usar un byte como delimitador para datos binarios serializados es una idea horrible, 13 es un valor perfectamente válido que puede ser parte de los datos serializados, no solo su “delimitador”.

Prefije cada bloque con tamaño en bytes y léalo en bloques.

Puede usar Marshal.SizeOf para obtener el tamaño nativo de una estructura. Esto solo funciona para estructuras y le aconsejo que establezca el atributo StructLayout.

Obtendré algo de información de los comentarios porque es sorprendente pero importante:

El CLR tiene facilidades de metadatos para hacer que el diseño nativo de una estructura o clase sea fijo. En C # esto solo es posible para estructuras. Pero las clases también pueden usarse de esa manera.

Puede convertir un tipo administrado en bytes en bytes si especifica SequentialLayout. http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.structlayoutattribute.aspx Esta instalación no es muy conocida pero existe, está especificada y es compatible. Cita: “Los atributos de diseño de clase (AutoLayout, SequentialLayout y ExplicitLayout) definen cómo se ubican en la memoria los campos de la instancia de la clase”.

Mire la enumeración System.Reflection.TypeAttributes. También define otros atributos de nivel CLR. C # no les da acceso a ellos, pero ilasm.exe lo hace.

Podría encontrar la causa de no serializar en absoluto el uso de este código en https://bytes.com/topic/c-sharp/answers/238927-object-size-memory

 var m = new System.IO.MemoryStream(); var b = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); b.Serialize(m, Obj); var size = Convert.ToDouble(m.Length);