BinaryFormatter イベント ソース

.NET 5 以降、BinaryFormatter には、オブジェクトのシリアル化または逆シリアル化が発生したタイミングを可視化できる、組み込みの EventSource が含まれています。 アプリでは、EventListener から派生した型を使用してこれらの通知をリッスンし、ログに記録できます。

この機能は SerializationBinder または ISerializationSurrogate に代わるものではなく、シリアル化または逆シリアル化されるデータを変更するために使用することはできません。 むしろ、このイベント システムは、シリアル化または逆シリアル化される型についての分析情報を提供することを目的としています。 また、BinaryFormatter インフラストラクチャへの意図しない呼び出し (サード パーティ製のライブラリ コードからの呼び出しなど) を検出するために使用することもできます。

イベントの説明

BinaryFormatter イベント ソースには、System.Runtime.Serialization.Formatters.Binary.BinaryFormatterEventSource という既知の名前があります。 リスナーは、6 つのイベントをサブスクライブできます。

SerializationStart イベント (id = 10)

BinaryFormatter.Serialize が呼び出され、シリアル化プロセスが開始されたときに発生します。 このイベントは、SerializationEnd イベントとペアになります。 SerializationStart イベントは、オブジェクトがそれ自体のシリアル化ルーチン内で BinaryFormatter.Serialize を呼び出す場合、再帰的に呼び出される可能性があります。

このイベントにはペイロードが含まれていません。

SerializationEnd イベント (id = 11)

BinaryFormatter.Serialize の処理が完了したときに発生します。 SerializationEnd が発生するたびに、最後のペアになっていない SerializationStart イベントが完了したことがわかります。

このイベントにはペイロードが含まれていません。

SerializingObject イベント (id = 12)

BinaryFormatter.Serialize によって非プリミティブ型をシリアル化する処理が行われているときに発生します。 BinaryFormatter インフラストラクチャでは特定の型 (stringint など) が特別なケースとして処理され、これらの型が検出されてもこのイベントは発生しません。 このイベントは、ユーザー定義型、および BinaryFormatter によってネイティブに認識されないその他の型に対して発生します。

このイベントは、SerializationStart イベントと SerializationEnd イベントの間で 0 回以上発生する可能性があります。

このイベントには、1 つの引数を持つペイロードが含まれます。

DeserializationStart イベント (id = 20)

BinaryFormatter.Deserialize が呼び出され、逆シリアル化プロセスが開始されたときに発生します。 このイベントは、DeserializationEnd イベントとペアになります。 DeserializationStart イベントは、オブジェクトがそれ自体の逆シリアル化ルーチン内で BinaryFormatter.Deserialize を呼び出す場合、再帰的に呼び出される可能性があります。

このイベントにはペイロードが含まれていません。

DeserializationEnd イベント (id = 21)

BinaryFormatter.Deserialize の処理が完了したときに発生します。 DeserializationEnd が発生するたびに、最後のペアになっていない DeserializationStart イベントが完了したことがわかります。

このイベントにはペイロードが含まれていません。

DeserializingObject イベント (id = 22)

BinaryFormatter.Deserialize によって非プリミティブ型を逆シリアル化する処理が行われているときに発生します。 BinaryFormatter インフラストラクチャでは特定の型 (stringint など) が特別なケースとして処理され、これらの型が検出されてもこのイベントは発生しません。 このイベントは、ユーザー定義型、および BinaryFormatter によってネイティブに認識されないその他の型に対して発生します。

このイベントは、DeserializationStart イベントと DeserializationEnd イベントの間で 0 回以上発生する可能性があります。

このイベントには、1 つの引数を持つペイロードが含まれます。

[高度な設定] 通知のサブセットをサブスクライブする

通知のサブセットのみをサブスクライブする必要があるリスナーでは、有効にするキーワードを選択できます。

  • Serialization = (EventKeywords)1:SerializationStartSerializationEnd、および SerializingObject イベントが発生します。
  • Deserialization = (EventKeywords)2:DeserializationStartDeserializationEnd、および DeserializingObject イベントが発生します。

EventListener の登録時にキーワード フィルターが指定されていない場合は、すべてのイベントが発生します。

詳細については、「System.Diagnostics.Tracing.EventKeywords」を参照してください。

サンプル コード

コード例を次に示します。

  • System.Console に書き込む EventListener から派生した型を作成します。
  • そのリスナーを BinaryFormatter によって生成された通知にサブスクライブします。
  • BinaryFormatter を使用してシンプルなオブジェクト グラフをシリアル化および逆シリアル化します。
  • 発生したイベントを分析します。
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]}");
                }
            }
        }
    }
}

上記のコードにより、次の例のような出力が生成されます。

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

このサンプルでは、コンソール ベースの EventListener によって、シリアル化が開始され、PersonBook のインスタンスがシリアル化された後、シリアル化が完了することがログに記録されます。 同様に、逆シリアル化が開始されると、PersonBook のインスタンスが逆シリアル化された後、逆シリアル化が完了します。

アプリでは次に、逆シリアル化された Person に含まれている値が出力され、オブジェクトが実際にシリアル化および逆シリアル化されたことが示されます。

関連項目

EventListener を使用して EventSource ベースの通知を受信する方法の詳細については、EventListenerに関する記事を参照してください。