BinaryFormatter Referencia de funcionalidad
Se BinaryFormatter introdujo por primera vez con la versión inicial de .NET Framework en 2002. Para comprender cómo reemplazar el uso de BinaryFormatter, ayuda a saber cómo BinaryFormatter funciona.
BinaryFormatter puede serializar cualquier instancia de cualquier tipo anotado con [Serializable]
o implementar la ISerializable interfaz.
Nombres de miembro
En el escenario más común, el tipo se anota con [Serializable]
y el serializador usa la reflexión para serializar todos los campos (públicos y no públicos), excepto los que se anotan con [NonSerialized]
. De forma predeterminada, los nombres de miembro serializados coincidirán con los nombres de campo del tipo. Esto llevó históricamente a incompatibilidades cuando incluso se cambia el nombre de los campos privados en [Serializable]
los tipos. Durante las migraciones fuera de BinaryFormatter, es necesario comprender cómo se controlaron y invalidaron los nombres de campo serializados.
Propiedades automáticas
En el caso de las propiedades implementadas automáticamente en C# ({ get; set; }
), BinaryFormatter serializará los campos de respaldo generados por el compilador de C#, no las propiedades. Los nombres de esos campos de respaldo serializados contienen caracteres de C# no válidos y no se pueden controlar. Un descompilador de C# (como https://sharplab.io/ o ILSpy) puede demostrar cómo se presentan las propiedades automáticas de C# al tiempo de ejecución.
[Serializable]
internal class PropertySample
{
public string Name { get; set; }
}
El compilador de C# traduce la clase anterior a:
[Serializable]
internal class PropertySample
{
private string <Name>k__BackingField;
public string Name
{
get
{
return <Name>k__BackingField;
}
set
{
<Name>k__BackingField = value;
}
}
}
En este caso, <Name>k__BackingField
es el nombre del miembro que BinaryFormatter
usa en la carga serializada. No es posible usar nameof
ni ningún otro operador de C# para obtener este nombre.
La interfaz ISerializable incluye el método GetObjectData, que permite a los usuarios controlar los nombres mediante uno de los métodos AddValue.
// Note lack of any special attribute.
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Name", this.Name);
}
Si se ha aplicado dicha personalización, también debe proporcionarse la información durante la deserialización. Esto es posible mediante el constructor de serialization, donde todos los valores se leen de la SerializationInfo con uno de los métodos Get
que proporciona.
private PropertySample(SerializationInfo info, StreamingContext context)
{
this.Name = info.GetString("Name");
}
Nota:
El nameof
operador no se usó aquí, ya que la carga se puede conservar y la propiedad se puede cambiar de nombre más adelante. Por lo tanto, incluso si se cambia el nombre (por ejemplo, FirstName
porque decide también introducir una propiedad LastName
), para permanecer compatible con versiones anteriores, la serialization debe seguir usando el nombre antiguo que podría haberse conservado en algún lugar.
enlazador Serialization
Se recomienda usar SerializationBinder para controlar la carga de clases y determinar qué clase cargar. Esto minimiza las vulnerabilidades de seguridad (por lo que solo se cargan los tipos permitidos, incluso si el atacante modifica la carga para deserializar y cargar otra cosa).
Para utilizar este tipo es necesario heredar de él y sobrescribir el método BindToType.
Idealmente, la lista de tipos serializables está cerrada porque significa que se pueden crear instancias de los tipos que ayudarán a reducir las vulnerabilidades de seguridad.