Origen del evento BinaryFormatter

A partir de .NET 5, BinaryFormatter incluye un elemento EventSource integrado que le permite ver cuándo se produce la serialización o deserialización de objetos. Las aplicaciones pueden usar tipos derivados de EventListener para escuchar estas notificaciones y registrarlas.

Esta funcionalidad no sustituye a SerializationBinder o ISerializationSurrogate y se puede usar para modificar los datos que se serializan o deserializan. Más bien, este sistema de eventos está diseñado para proporcionar información sobre los tipos que se serializan o deserializan. También se puede usar para detectar llamadas imprevistas en la infraestructura BinaryFormatter, como las que se originan desde código de biblioteca de terceros.

Descripción de eventos

El origen del evento BinaryFormatter tiene el nombre conocido System.Runtime.Serialization.Formatters.Binary.BinaryFormatterEventSource. Los agentes de escucha pueden suscribirse a seis eventos.

Evento SerializationStart (id. = 10)

Se genera cuando se ha llamado a BinaryFormatter.Serialize y se ha iniciado el proceso de serialización. Este evento se empareja con el evento SerializationEnd. Se puede llamar al evento SerializationStart de forma recursiva si un objeto llama a BinaryFormatter.Serialize dentro de su propia rutina de serialización.

Este evento no contiene una carga útil.

Evento SerializationEnd (id. = 11)

Se genera cuando BinaryFormatter.Serialize ha completado su trabajo. Cada aparición de SerializationEnd indica la finalización del último evento de SerializationStart no emparejado.

Este evento no contiene una carga útil.

Evento SerializingObject (id. = 12)

Se genera cuando BinaryFormatter.Serialize está en el proceso de serializar un tipo no primitivo. La infraestructura BinaryFormatter tiene determinados tipos de casos especiales (como string y int) y no genera un evento cuando se encuentran estos tipos. Este evento se genera para tipos definidos por el usuario y otros tipos que BinaryFormatter no entiende de forma nativa.

Este evento puede producirse cero o más veces entre los eventos SerializationStart y SerializationEnd.

Este evento contiene una carga con un argumento:

Evento DeserializationStart (id. = 20)

Se genera cuando se ha llamado a BinaryFormatter.Deserialize y se ha iniciado el proceso de deserialización. Este evento se empareja con el evento DeserializationEnd. Se puede llamar al evento DeserializationStart de forma recursiva si un objeto llama a BinaryFormatter.Deserialize dentro de su propia rutina de deserialización.

Este evento no contiene una carga útil.

Evento DeserializationEnd (id. = 21)

Se genera cuando BinaryFormatter.Deserialize ha completado su trabajo. Cada aparición de DeserializationEnd indica la finalización del último evento de DeserializationStart no emparejado.

Este evento no contiene una carga útil.

Evento DeserializingObject (id. = 22)

Se genera cuando BinaryFormatter.Deserialize está en el proceso de deserializar un tipo no primitivo. La infraestructura BinaryFormatter tiene determinados tipos de casos especiales (como string y int) y no genera un evento cuando se encuentran estos tipos. Este evento se genera para tipos definidos por el usuario y otros tipos que BinaryFormatter no entiende de forma nativa.

Este evento puede producirse cero o más veces entre los eventos DeserializationStart y DeserializationEnd.

Este evento contiene una carga con un argumento.

[Avanzado] Suscripción a un subconjunto de notificaciones

Los agentes de escucha que deseen suscribirse solo a un subconjunto de notificaciones pueden elegir las palabras clave que desean permitir.

  • Serialization = (EventKeywords)1: genera los eventos SerializationStart, SerializationEnd y SerializingObject.
  • Deserialization = (EventKeywords)2: genera los eventos DeserializationStart, DeserializationEnd y DeserializingObject.

Si no se proporciona ningún filtro de palabras clave durante el registro de EventListener, se generan todos los eventos.

Para obtener más información, vea System.Diagnostics.Tracing.EventKeywords.

Código de ejemplo

El código siguiente:

  • crea un tipo derivado de EventListener que escribe en System.Console,
  • suscribe ese agente de escucha a las notificaciones generadas por BinaryFormatter,
  • serializa y deserializa un gráfico de objetos simple mediante BinaryFormatter, y
  • analiza los eventos que se han generado.
using System;
using System.Diagnostics.Tracing;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace BinaryFormatterEventSample
{
    class Program
    {
        static EventListener? _globalListener = null;

        static void Main(string[] args)
        {
            // First, set up the event listener.
            // Note: We assign it to a static field so that it doesn't get GCed.
            // We also provide a callback that subscribes this listener to all
            // events produced by the well-known BinaryFormatter source.

            _globalListener = new ConsoleEventListener();
            _globalListener.EventSourceCreated += (sender, args) =>
            {
                if (args.EventSource?.Name ==
                    "System.Runtime.Serialization.Formatters.Binary.BinaryFormatterEventSource")
                {
                    ((EventListener?)sender)?
                        .EnableEvents(args.EventSource, EventLevel.LogAlways);
                }
            };

            // Next, create the Person object and serialize it.

            Person originalPerson = new Person()
            {
                FirstName = "Logan",
                LastName = "Edwards",
                FavoriteBook = new Book()
                {
                    Title = "A Tale of Two Cities",
                    Author = "Charles Dickens",
                    Price = 10.25m
                }
            };

            byte[] serializedPerson = SerializePerson(originalPerson);

            // Finally, deserialize the Person object.

            Person rehydratedPerson = DeserializePerson(serializedPerson);

            Console.WriteLine
                ($"Rehydrated person {rehydratedPerson.FirstName} {rehydratedPerson.LastName}");
            Console.Write
                ($"Favorite book: {rehydratedPerson.FavoriteBook?.Title} ");
            Console.Write
                ($"by {rehydratedPerson.FavoriteBook?.Author}, ");
            Console.WriteLine
                ($"list price {rehydratedPerson.FavoriteBook?.Price}");
        }

        private static byte[] SerializePerson(Person p)
        {
            MemoryStream memStream = new MemoryStream();
            BinaryFormatter formatter = new BinaryFormatter();
#pragma warning disable SYSLIB0011 // BinaryFormatter.Serialize is obsolete
            formatter.Serialize(memStream, p);
#pragma warning restore SYSLIB0011

            return memStream.ToArray();
        }

        private static Person DeserializePerson(byte[] serializedData)
        {
            MemoryStream memStream = new MemoryStream(serializedData);
            BinaryFormatter formatter = new BinaryFormatter();

#pragma warning disable SYSLIB0011 // Danger: BinaryFormatter.Deserialize is insecure for untrusted input
            return (Person)formatter.Deserialize(memStream);
#pragma warning restore SYSLIB0011
        }
    }

    [Serializable]
    public class Person
    {
        public string? FirstName;
        public string? LastName;
        public Book? FavoriteBook;
    }

    [Serializable]
    public class Book
    {
        public string? Title;
        public string? Author;
        public decimal? Price;
    }

    // A sample EventListener that writes data to System.Console.
    public class ConsoleEventListener : EventListener
    {
        protected override void OnEventWritten(EventWrittenEventArgs eventData)
        {
            base.OnEventWritten(eventData);

            Console.WriteLine($"Event {eventData.EventName} (id={eventData.EventId}) received.");
            if (eventData.PayloadNames != null)
            {
                for (int i = 0; i < eventData.PayloadNames.Count; i++)
                {
                    Console.WriteLine($"{eventData.PayloadNames[i]} = {eventData.Payload?[i]}");
                }
            }
        }
    }
}

El código anterior genera una salida similar al ejemplo siguiente:

Event SerializationStart (id=10) received.
Event SerializingObject (id=12) received.
typeName = BinaryFormatterEventSample.Person, BinaryFormatterEventSample, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Event SerializingObject (id=12) received.
typeName = BinaryFormatterEventSample.Book, BinaryFormatterEventSample, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Event SerializationStop (id=11) received.
Event DeserializationStart (id=20) received.
Event DeserializingObject (id=22) received.
typeName = BinaryFormatterEventSample.Person, BinaryFormatterEventSample, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Event DeserializingObject (id=22) received.
typeName = BinaryFormatterEventSample.Book, BinaryFormatterEventSample, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Event DeserializationStop (id=21) received.
Rehydrated person Logan Edwards
Favorite book: A Tale of Two Cities by Charles Dickens, list price 10.25

En este ejemplo, la clase EventListener basada en consola registra que la serialización se inicia, se serializan las instancias de Person y Book y, luego, se completa la serialización. Del mismo modo, una vez que se ha iniciado la deserialización, se deserializan las instancias de Person y Book y, luego, se completa la operación.

A continuación, la aplicación imprime los valores contenidos en la instancia de Person deserializada para demostrar que el objeto en realidad se serializó y deserializó correctamente.

Vea también

Para más información sobre el uso de EventListener para recibir notificaciones basadas en EventSource, consulte la claseEventListener.