Compartilhar via


BinaryFormatter referência de funcionalidade

O BinaryFormatter foi introduzido pela primeira vez com a versão inicial do .NET Framework em 2002. Para entender como substituir o uso de BinaryFormatter, é útil saber como BinaryFormatter funciona.

BinaryFormatter pode serializar qualquer instância de qualquer tipo que seja anotada com [Serializable] ou implementa a interface ISerializable.

Nomes dos membros

No cenário mais comum, o tipo é anotado com [Serializable] e o serializador usa a reflexão para serializar todos os campos (públicos e não públicos), exceto aqueles que são anotados com [NonSerialized]. Por padrão, os nomes de membros serializados corresponderão aos nomes de campo do tipo. Isso historicamente levou a incompatibilidades quando até mesmo campos privados são renomeados em tipos [Serializable]. Durante as migrações para fora do BinaryFormatter, torna-se necessário entender como os nomes de campo serializados foram manipulados e substituídos.

Propriedades automáticas do C#

Para propriedades implementadas automaticamente em C# ({ get; set; }), BinaryFormatter serializará os campos de suporte gerados pelo compilador C#, não as propriedades. Os nomes desses campos de suporte serializados contêm caracteres C# ilegais e não podem ser controlados. Um descompilador C# (como https://sharplab.io/ ou ILSpy) pode demonstrar como as propriedades automáticas do C# são apresentadas ao runtime.

[Serializable]
internal class PropertySample
{
    public string Name { get; set; }
}

A classe anterior é convertida pelo compilador C# para:

[Serializable]
internal class PropertySample
{
    private string <Name>k__BackingField;

    public string Name
    {
        get
        {
            return <Name>k__BackingField;
        }
        set
        {
            <Name>k__BackingField = value;
        }
    }
}

Nesse caso, <Name>k__BackingField é o nome do membro que BinaryFormatter usa na carga serializada. Não é possível usar nameof ou qualquer outro operador C# para obter esse nome.

A ISerializable interface vem com um método GetObjectData que permite aos usuários controlar os nomes, usando um dos métodos AddValue.

// Note lack of any special attribute.
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
    info.AddValue("Name", this.Name);
}

Se essa personalização tiver sido aplicada, as informações também precisarão ser fornecidas durante a desserialização. Isso é possível usando o construtor de serialização , em que todos os valores são lidos de SerializationInfo com um dos métodos Get que ele fornece.

private PropertySample(SerializationInfo info, StreamingContext context)
{
    this.Name = info.GetString("Name");
}

Observação

O operador nameof não foi usado propositalmente aqui, pois a carga pode ser mantida e a propriedade pode ser renomeada posteriormente. Portanto, mesmo que seja renomeado (digamos para FirstName porque você decide também introduzir uma propriedade LastName), para permanecer compatível com versões anteriores, a serialização ainda deve usar o nome antigo que poderia ter sido persistido em algum lugar.

Serialization fichário

É recomendável usar SerializationBinder para controlar o carregamento de classe e determinar qual classe carregar. Isso minimiza as vulnerabilidades de segurança (portanto, apenas os tipos permitidos são carregados, mesmo que o invasor modifique a carga para desserializar e carregar outra coisa).

O uso desse tipo requer herdar dele e substituir o método BindToType.

De modo ideal, a lista de tipos serializáveis é fechada porque significa que você sabe quais tipos podem ser instanciados, o que ajudará a reduzir as vulnerabilidades de segurança.