Quais são as deficiências da serialização .Net baseada em BinaryFormatter?

Quais são as deficiências da serialização .Net baseada em BinaryFormatter ? (Desempenho, flexibilidade, restrições)

Por favor, acompanhe sua resposta com algum código, se possível.

Exemplo:

Os objects personalizados que estão sendo serializados devem ser decorados com o atributo [Serializable] ou implementar a interface ISerializable.

Exemplo menos óbvio:

Tipos anônimos não podem ser serializados.

Se você quer dizer BinaryFormatter :

  • sendo baseado em campos, é muito intolerante a versão; alterar detalhes de implementação privada e quebra (mesmo apenas alterando-a para uma propriedade implementada automaticamente )
  • não é compatível com outras plataformas
  • não é muito amigável para novos campos
  • é específico da assembly (os metadados são queimados)
  • é específico para MS / .NET (e possivelmente versão específica para .NET)
  • não é obfuscation-safe
  • não é especialmente rápido ou pequeno resultado
  • não funciona em estruturas leves (CF? / Silverlight)
  • tem um hábito deprimente de puxar coisas que você não esperava (geralmente via event )

Passei muito tempo nessa área, inclusive escrevendo uma implementação (gratuita) da API de serialização de “buffers de protocolo” do Google para .NET; protobuf-net

Isto é:

  • saída menor e mais rápida
  • Compatível com outras implementações
  • extensível
  • contrato-baseado
  • ofuscação segura
  • assembly independente
  • é um padrão aberto documentado
  • funciona em todas as versões do .NET (ressalva: não testado no Micro Framework)
  • tem ganchos para conectar ISerializable (para remoting etc) e WCF

Dado qualquer object random, é muito difícil provar se ele é realmente serializável .

O version control de dados é tratado por meio de atributos. Se você não está preocupado com o version control, isso não é problema. Se você é, é um problema enorme.

O problema com o esquema de atributos é que ele funciona muito bem para muitos casos triviais (como adicionar uma nova propriedade), mas é interrompido rapidamente quando você tenta fazer algo como replace dois valores de enum por um novo valor de enum diferente (ou qualquer número de cenários comuns que vem com dados persistentes de longa duração).

Eu poderia entrar em muitos detalhes descrevendo os problemas. No final, escrever seu próprio serializador é muito fácil se você precisar …

Se você alterar o object que está sendo serializado, todos os dados antigos que você serializou e armazenou serão quebrados. Se você armazenou em um database ou mesmo em XML, é mais fácil converter dados antigos em novos.

Não é garantido que você possa serializar objects entre diferentes Frameworks (como 1.0, 1.1, 3.5) ou mesmo diferentes implementações do CLR (Mono), novamente, o XML é melhor para essa finalidade.

Outra questão que me veio à mente:

As classs XmlSerializer estão localizadas em um local completamente diferente dos formatadores genéricos de tempo de execução. E, embora sejam muito semelhantes ao uso, o XmlSerializer não implementa a interface IFormatter. Você não pode ter o código que permite que você simplesmente troque o formatador de serialização para dentro ou para fora em tempo de execução entre BinaryFormatter, XmlSerializer ou um formatador personalizado sem passar por alguns aros extras.

Os tipos que estão sendo serializados devem ser decorados com o atributo [Serializable].

Se você quer dizer variables ​​em uma class, você está errado. Variáveis ​​/ propriedades públicas são serializadas automaticamente

Um pouco menos óbvio é que o desempenho é muito ruim para a serialização de objects.

Exemplo

Tempo para serializar e desserializar 100.000 objects na minha máquina:

 Time Elapsed 3 ms Full Serialization Cycle: BinaryFormatter Int[100000] Time Elapsed 1246 ms Full Serialization Cycle: BinaryFormatter NumberObject[100000] Time Elapsed 54 ms Full Serialization Cycle: Manual NumberObject[100000] 

Neste exemplo simples, serializar um object com um único campo Int leva 20x mais devagar do que fazê-lo manualmente. Concedido, há algumas informações de tipo no stream serializado. Mas isso dificilmente explica a desaceleração do 20X.

Eu concordo com a última resposta. O desempenho é muito ruim. Recentemente, minha equipe de codificadores terminou de converter uma simulação do padrão C ++ para C ++ / CLI. Em C ++, nós tínhamos um mecanismo de persistência escrito à mão, que funcionava razoavelmente bem. Decidimos usar o mecanismo de serialização, em vez de rewrite o antigo mecanismo de persistência.
A simulação antiga, com uma pegada de memory entre 1/2 e 1 Gig e a maioria dos objects tendo pointers para outros objects, e 1000 de objects em tempo de execução, persistiria em um arquivo binário de cerca de 10 a 15 Meg em menos de um minuto. A restauração do arquivo foi comparável.
Usando os mesmos arquivos de dados (rodando lado-a-lado) o desempenho de execução do C ++ / CLI é cerca de duas vezes o C ++, até que façamos a persistência (serialização na nova versão) A gravação leva entre 3 e 5 minutos, lendo em leva entre 10 e 20. O tamanho do arquivo dos arquivos serializados é cerca de 5 vezes o tamanho dos arquivos antigos. Basicamente, vemos um aumento de 19 vezes no tempo de leitura e um aumento de 5 vezes no tempo de gravação. Isso é inaceitável e estamos procurando maneiras de corrigir isso.

Ao examinar os arquivos binários, descobri algumas coisas: 1. Os dados de tipo e assembly são escritos em texto claro para todos os tipos. Isso é ineficiente em relação ao espaço. 2. Todo object / instância de cada tipo tem as informações de tipo / assembly inchadas escritas. Uma coisa que fizemos em nossa mão persistence mechansim foi escrever uma tabela de tipos conhecida. Quando descobrimos tipos por escrito, procuramos sua existência nessa tabela. Se não existisse, uma input foi criada com as informações de tipo e um índice atribuído. Então passamos o tipo infor como um inteiro. (tipo, dados, tipo, dados) Este ‘truque’ reduziria tremendamente o tamanho. Isso pode exigir a passagem dos dados duas vezes, no entanto, um processo “on-the-fly” poderia ser desenvolvido, além de adicioná-lo à tabela, empurrando para o stream, se pudéssemos garantir a ordem de ressonância do stream .

Eu estava esperando reimplantar algumas das serializações centrais para otimizá-lo dessa maneira, mas, infelizmente, as classs estão seladas! Ainda podemos encontrar uma maneira de improvisar.

Outra situação faz com que o BinaryFormatter lance uma exceção.

 [Serializable] class SerializeMe { public List _dataList; public string _name; } [Serializable] class Data { public int _t; } 

Imagine SerializeMe é serializado hoje. Amanhã decidimos que não precisamos mais da class Data e removê-la. Assim, modificamos a class SerializeMe para remover a lista. Agora é impossível desserializar a versão antiga de um object SerializeMe.

A solução é criar um BinaryFormatter personalizado para ignorar corretamente as classs extras ou manter os dados da class com uma definição vazia (não é necessário manter o membro List).