Partilhar 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 do BinaryFormatter, ajuda a saber como BinaryFormatter funciona.

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

Nomes dos membros

No cenário mais comum, o tipo é anotado com [Serializable] e o serializador usa 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 [Serializable] tipos. Durante migrações para longe do , torna-se necessário entender como os nomes de BinaryFormattercampo 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 tempo de execução.

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

A classe anterior é traduzida 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 GetObjectData método que permite aos usuários controlar os nomes, usando um dos AddValue métodos.

// 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 serialization construtor, onde todos os valores são lidos SerializationInfo com um dos Get métodos que ele fornece.

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

Nota

O nameof operador não foi usado propositalmente aqui, pois a carga útil pode ser persistente e a propriedade pode ser renomeada em um momento posterior. Então, mesmo que ele seja renomeado (digamos porque FirstName você decide também introduzir uma LastName propriedade), para permanecer retrocompatível, o serialization ainda deve usar o nome antigo que poderia ter sido persistido em algum lugar.

Serialization aglutinante

Recomenda-se usar SerializationBinder para controlar o carregamento de classe e determinar qual classe carregar. Isso minimiza as vulnerabilidades de segurança (para que apenas os tipos permitidos sejam carregados, mesmo que o invasor modifique a carga para desserializar e carregar outra coisa).

Usar esse tipo requer herdar dele e substituir o BindToType método.

Idealmente, 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.