17. 可変長と固定長
Byteprocでは固定長のデータ型と可変長のデータ型を明確に区別して扱います。
固定長のデータ型とは、int型やdouble型などのように、どんな値が設定されてもデータサイズが常に同じになるタイプのデータ型のことを指します(int型なら4Byte、double型なら8Byteになります)。
可変長のデータ型は、Listや文字列のように設定される内容によってデータサイズが変わるタイプのデータ型を指します。
Byteprocの処理において、基本的に固定長のデータ型の方が速度的に有利な場合が多いです。そのため、一部の可変長型はサイズの上限をあらかじめ指定することで固定長として扱えるようになっています。(ただし、固定長扱いにした場合、常に上限いっぱいのデータサイズを確保しますので、可変長扱いにするよりもデータサイズが大きくなる場合が多いです)
コンバータ―/プロセッサーは基本的に固定長用と可変長用が対で定義されていて、固定長のコンバータ―/プロセッサーの名前は、可変長のコンバータ―/プロセッサーの名前の最後に「FL」を付けた形になります。
以下はByteprocで定義されているコンバーター/プロセッサーのインターフェイス一覧です。
コンバーターのインターフェイス一覧
プロセッサーのインターフェイス一覧
ビルダーでコンバーター/プロセッサーを構築する際、追加したフィールドが固定長のフィールドのみであれば、出力されるコンバーター/プロセッサーを固定長のインターフェイスで受け取ることができます。
以下のサンプルでは、全てのフィールドが固定長のクラスのコンバーターを作成しています。
L.8で固定長型のインターフェイスでコンバーターを受け取っています。
次に、文字列のサイズの上限を指定することで固定長化するサンプルを紹介します。
L.11で文字列のサイズの上限を指定しているので、全てのフィールドが固定長扱いとなり、固定長型のインターフェイスでコンバーターを受け取ることが出来ています。
尚、全ての固定長型インターフェイスは、対となる可変長型のインターフェイスを継承していますので、上記サンプルのように全てのフィールドが固定長の場合でも、可変長型のインターフェイスで受け取ることができますが、固定長のインターフェイスで受け取ることで速度向上につながる場合がありますので、パフォーマンスチューニングの際は固定長のインターフェイスで受け取ることを検討してみてください。
ビルダーで構築した結果が固定長なのか判断できない場合は、試しにコンバーター名の後ろにFLを付けてみて下さい。構築結果が固定長の場合はそのままコンパイルできますが、可変長の場合はコンパイルエラーになります。
中間バイトコンバーター/プロセッサーは対象オブジェクトにバイトデータを持たせて、フィールドに対してアクセスされたタイミングでシリアライズ/デシリアライズを実行することで、対象オブジェクト全体のシリアライズ/デシリアライズ実行時の作業量を減らし、高速化を図るタイプのシリアライザ―で、IntermediateByteDataConverterBuilderを使って構築します。
以下はIntermediateByteDataConverterBuilderを使った中間バイトコンバーターの構築サンプルです。
16. 中間バイトコンバーター/プロセッサーの機能と構築方法
中間コンバーター系のコンバーターは、通常のコンバーターと比較して以下の点が異なります。
-
対象のデータ型はIntermediateDataを継承する
-
処理対象のフィールドは全てIField<TValue>で定義する ※ TValueはフィールドに持たせる値の型
-
対象オブジェクトはコンバーターかプロセッサーのCreateDataメソッドでインスタンスを作成する必要がある
-
Create<TTarget>(Func<TTarget> createInstance, IntermediateFieldType defaultIntermediateFieldType, ByteOrderType byteOrderType, IntermediateByteDataConverterMode mode)
-
Create<TTarget>(Func<TTarget> createInstance, IntermediateFieldType defaultIntermediateFieldType, ByteOrderType byteOrderType)
-
Create<TTarget>(Func<TTarget> createInstance, IntermediateFieldType defaultIntermediateFieldType)
-
Create<TTarget>(IntermediateFieldType defaultIntermediateFieldType, ByteOrderType byteOrderType, IntermediateByteDataConverterMode mode)
-
Create<TTarget>(IntermediateFieldType defaultIntermediateFieldType, ByteOrderType byteOrderType)
-
Create<TTarget>(IntermediateFieldType defaultIntermediateFieldType)
Createメソッドには以下のオーバーロードがあります。
◆ ビルダーの解説
Add<TField>(IntermediateFieldType intermediateFieldType, Action<TTarget, IField<TField>> setter, IProcessor<TField> processor)
中間コンバータ―では他のコンバーターと異なり、IField<TValue>型でメンバ変数を定義する必要があり、Addメソッドではこのメンバ変数に対して中間フィールドオブジェクトを設定する方法を指定することになります。(そのため、Addメソッドの引数にはgetterを指定しません)
尚、Createメソッドで指定するIntermediateFieldTypeはデフォルト値として作用し、追加する全てのフィールドに適用されますが、フィールドごとに異なるIntermediateFieldTypeを設定することも可能です。その場合、以下のAddメソッドを使用します。
コンバーターは以下のメソッドで出力します。
IntermediateByteDataConverterBuilderで構築したコンバーターはIVersioningIntermediateByteDataConverter<TTarget>インターフェイスで出力されます。
これは以下のインターフェイスを継承しています。
◆ コンバーターの解説
-
IDeserializer<TTarget>
-
IReusableDeserializer<TTarget>
-
IProcessor<TTarget>
-
IReusableValueProcessor<TTarget>
-
IClassProcessor<TTarget>
-
IIntermediateProcessor<TTarget>
IntermediateByteDataConverterBuilderで構築したプロセッサーはIIntermediateByteDataProcessor<TTarget>インターフェイスで出力されます。
これは以下のインターフェイスを継承しています。
◆ プロセッサーの解説
IIntermediateByteDataProcessor<TTarget>は以下のプロパティ、メソッドを持っています。
・int GetByteSize(TTarget target)
引数に指定されたオブジェクトをシリアライズしたときのサイズを返します。
第1引数:サイズを取得するオブジェクト。
戻り値:サイズ
・void Serialize(byte[] bytes, ref int offset, TTarget target)
シリアライズを実行します。
第1引数:シリアライズ後のデータが設定されます。シリアライズに必要なサイズのbyte配列を作成して渡す必要があります。
第2引数:シリアライズしたデータの格納開始位置を指定します。実行後はシリアライズ終了位置+1が格納されます。
第3引数:シリアライズ対象のオブジェクトを指定します。
・int GetByteSize(byte[] bytes, int offset)
このプロセッサーの対象データのサイズを返します。
第1引数:シリアライズデータが格納されたbyte配列。
第2引数:このプロセッサーの対象データの開始位置。
戻り値:サイズ
・TTarget Deserialize(byte[] bytes, ref int offset)
デシリアライズを実行します。
第1引数:シリアライズデータが格納されたbyte配列。
第2引数:このプロセッサーの対象データの開始位置。実行後はデシリアライズ終了位置+1が格納されます。
戻り値:デシリアライズされたオブジェクト。
・TTarget Deserialize(TTarget target, byte[] bytes, ref int offset)
インスタンスを再利用してデシリアライズを実行します。
第1引数:再利用するインスタンス。
第2引数:シリアライズデータが格納されたbyte配列。
第3引数:このプロセッサーの対象データの開始位置。実行後はデシリアライズ終了位置+1が格納されます。
戻り値:デシリアライズされたオブジェクト。
・IntermediateByteDataSubClessParameter<TTargetSub> CreateSubClassParameter<TTargetSub>()
このプロセッサーが持つ、対象フィールドの情報をSubClessParameterに格納して返します。
これは、このプロセッサーが処理対象とするクラスを継承したクラスのプロセッサーを作成する際にビルダーのAddBaseClassParameterに指定することで、このプロセッサーが持つ対象フィールドの情報を引き継ぐことができます。
総称型引数:引き継ぎ先のプロセッサーが処理対象とするデータ型。(このプロセッサーが対象とするデータ型の派生型のみ指定可)
戻り値:このプロセッサーが持つ対象フィールドの情報
注意:この機能はiOS環境で正常に動作しません。詳しくは「Note 1. iOS環境の制限」を参照してください
・TTarget CreateData()
対象データ型の新しいインスタンスを作成し、フィールドを初期化します。
IVersioningIntermediateByteDataConverter<TTarget>は以下のプロパティ、メソッドを持っています。
・IVersioningIntermediateByteDataProcessor<TTarget> Processor
このコンバーターの変換処理を担うプロセッサーを返します。
・IVersioningIntermediateByteDataConverter<TTarget> AddVersion(int versionNumber, IDeserializer<TTarget> deserializer)
このコンバーターに別のバージョンのデシリアライザ―を追加します。
第1引数:バージョン番号。
第2引数:追加するデシリアライザ―。
戻り値:呼び出したオブジェクト(メソッドチェイン)。
・byte[] Serialize(TTarget target)
シリアライズを実行します。
第1引数:シリアライズ対象のオブジェクト。
戻り値:シリアライズ結果のbyte配列。
・byte[] SerializeWithoutTrimming(TTarget target)
シリアライズを実行します。ただし、結果をトリムしません。
第1引数:シリアライズ対象のオブジェクト。
戻り値:シリアライズ結果のbyte配列。
・TTarget Deserialize(byte[] bytes)
デシリアライズを実行します。
第1引数:シリアライズデータが格納されたbyte配列。
戻り値:デシリアライズされたオブジェクト。
・TTarget Deserialize(TTarget value, byte[] bytes)
インスタンスを再利用してデシリアライズを実行します。
第1引数:再利用するインスタンス。
第2引数:シリアライズデータが格納されたbyte配列。
戻り値:デシリアライズされたオブジェクト。
・TTarget CreateData()
対象データ型の新しいインスタンスを作成し、フィールドを初期化します。
IIntermediateByteDataProcessor<TTarget> ToProcessor()
プロセッサーは以下のメソッドで出力します。
このメソッドが出力するコンバーターはバージョン管理コンバーターです。
引数でバージョン番号を指定することが可能で、引数を省略した場合のバージョン番号は0になります。
IVersioningIntermediateByteDataConverter<TTarget> ToConverter(int versionNumber)
これらのパラメーターの内、IntermediateFieldTypeに何を指定したかによって、フィールドごとのシリアライズ/デシリアライズの動作が変わります。指定可能な値は以下の通りです。
Addメソッドは以下のような形式となります。(型ごとの実際のメソッド名は、標準対応のデータ型一覧を参照してください)
Add型名(Action<TTarget, IField<TField>> setter)
-
IConverter<TTarget>
-
IClassConverter<TTarget>
-
IVersioningConverter<TTarget>
-
IVersioningClassConverter<TTarget>
-
IIntermediateConverter<TTarget>
-
IIntermediateByteDataConverter<TTarget>
ByteClapの世界へようこそ!
ByteClapはUnity™で利用できるC#向けの静的シリアライザー構築ライブラリです。
簡単なプログラムを記述するだけで、あなたのゲームに超高速・高機能なシリアライズ処理を組込むことができます。
それでは、Byteprocの使い方を見ていきましょう!
Unity Asset StoreでByteClapを購入し、ダウンロードしてください。
Unity Asset Store: https://www.assetstore.unity3d.com/jp/#!/content/93858
ダウンロードが完了したら、すぐに使い始めることが出来ます!
1. インストール方法
2. 基本
ByteClapは静的シリアライザーを簡単に構築するためのライブラリです。
「静的シリアライザー」とは、リフレクションや式木などの動的コード解析・生成技術を使わずに、シリアライズ処理を実現するということです。
そのため、シリアライズのために必要な情報をコーディングする手間がありますが、動的なシリアライズ技術に比べて大幅に高速な処理を実現することができます。
また、シリアライズ設定をコードで記述するので、細かいカスタマイズやチューニングも容易に実現することができます。
さて、以下のサンプルコードをご覧ください
これはByteClapを使ったシリアライズ/デシリアライズ処理の一連のフローを俯瞰できる簡単なサンプルです。
UnityのHierarchyウィンドウで適当なゲームオブジェクトを作成し、このスクリプトをアタッチして実行ボタンを押すと、コンソールに「num = 123, str = abc」と表示されます(スクリプトの名前はSample01.csにして下さい)。
記述内容を簡単に説明します。
L.2:usingディレクティブでOmanju.ByteClap名前空間を指定しています。ByteClapの基本的な機能はこの名前空間に収められています。
L.4~12:シリアライズの対象となるクラスです。int型のフィールドnumとstring型のフィールドstrを持ち、コンバーターをstaticに持っています。
L.8~11:ビルダーを使ってSampleClass用のコンバーターを定義しています。
L.16~23:SampleClassを初期化し、コンバーターを使ってシリアライズ、デシリアライズし、結果をデバッグログで出力しています。
このように、ByteClapではビルダーと呼ばれる機能を使ってコンバーターを定義し、コンバーターを使ってシリアライズとデシリアライズを実行します。
ビルダーを使用することで非常に簡単にコンバーターを定義できるので、静的シリアライザーという形式をとりながらも、コーディングのコストを低く抑えることができます。
3. コンバーターとプロセッサー
前節で、コンバーターを使ってシリアライズ/デシリアライズを実行すると説明しましたが、このコンバーターは内部にプロセッサーと呼ばれるオブジェクトを持っていて、シリアライズ/デシリアライズ処理の実体はこのプロセッサーに定義されています。
コンバーターはプロセッサーを使用する上で必要となる面倒な前処理などを隠蔽し、簡単に使用できるようにするためのラッパー的な役割しかありません。
プロセッサーはByteClapにおいて最も重要かつ基本的な機能です。1つのプロセッサーは1つのデータ型を対象としており、ByteClapでシリアライズ/デシリアライズできるデータ型は、全て対応するプロセッサーが定義されています。
また、前節の例ではビルダーを使ってコンバーターを出力していましたが、ビルダーはプロセッサーを出力することもできます。
プロセッサーは、ビルダーを使った構築処理で使用したり、他のコンバーターやプロセッサーを使用する際のパラメーターになるなど、様々な場面で使用することになります。
ByteClapライブラリに定義された名前空間の内、ユーザが使用する目的で作られた機能が定義されている名前空間は以下の通りです。
4. 名前空間
上記の表で紹介した名前空間以外にも、「.Core」で終わる名前空間がいくつかあります。
これは、ByteClapライブラリが内部で使用するために作成された機能が定義されている名前空間です。バグ修正やマイナーアップデートで予告なく機能が増減したり、インターフェイスが変更される場合がありますので、直接使用することは推奨しません(いまのところドキュメントも用意していません)。
もし直接使用する場合は、ご留意ください。
◆ Core名前空間について
この内、Omanju.ByteClap.LogsとOmanju.ByteClap.Selectorsは、はじめはあまり使う機会がないと思います。
基本はusingディレクティブにOmanju.ByteClapを指定し、プロセッサーを直接扱う場合はOmanju.ByteClap.Processorsも指定するような使い方になると思います。
ByteClapを使用してシリアライズ/デシリアライズ処理を行うには、ビルダーを使ってコンバーターやプロセッサーを定義することになりますが、これらは用途の異なるいくつかの種類があります。以下はその一覧です。
5. プロセッサー、コンバーター、ビルダーの種類
ByteClapでは、プリミティブ型やよく使用されるコレクション型などの、いくつかのデータ型についてはあらかじめプロセッサーを定義し、簡単に使用できるようにしてあります。
ビルダーを使用して構築されたコンバーター/プロセッサー内でも、各フィールドの変換処理にはこれらのプロセッサーが使用されます。
標準で定義されているプロセッサーは標準対応のデータ型一覧を参照してください。
6. 標準で定義されているプロセッサー
7. フィルタ系プロセッサー
ByteClapで定義されているプロセッサーの中には、他のプロセッサーをセットすることで、セットしたプロセッサーに対して機能を追加する働きをするものがあります。
以下はその一覧です。
これらの他に、ビルダーによっては特有の引数を持つものもあります。
詳しくはビルダー別の解説と、APIドキュメントを参照してください。
② フィールドの追加
ビルダーのCreate()メソッドで、構築開始を宣言します。
Create()メソッドにはいくつかの引数を指定できるオーバーロードがあり、構築するコンバーター/プロセッサーのカスタマイズができます。
Create()メソッドの引数に指定できる値の内、代表的なものを以下の一覧にあげます。
public static readonly IConverter<SampleClass> converter = ClassConverterBuilder.Create<SampleClass>()
.AddInt(o => o.num, (o,v) => o.num = v)
.AddString(o => o.str, (o,v) => o.str = v)
.ToConverter();
ビルダーを使用したコンバーター/プロセッサーの構築は、以下の3ステップで行います。
① 構築開始の宣言
② フィールドの追加
③ コンバーター/プロセッサーの出力
以下、Sample01.csのコンバータ定義をベースに、詳細を解説します。
① 構築開始の宣言
8. ビルダーの基本
public static readonly IConverter<SampleClass> converter = ClassConverterBuilder.Create<SampleClass>()
.AddInt(o => o.num, (o,v) => o.num = v)
.AddString(o => o.str, (o,v) => o.str = v)
.ToConverter();
Add型名(Func<TTarget, TField> getter, Action<TTarget, TField> setter)
対象オブジェクトの内、シリアライズの対象にしたいフィールドを追加している部分です。
ビルダーの各メソッドはメソッドチェイン方式を採用しているので、Create()メソッドの呼び出し後、「.」で続けてフィールド設定用のメソッドを呼び出しています。
フィールドは1つずつAddメソッドで追加する必要があります。
追加時に使用するAddメソッドは、概ね以下のような形式をとります。
public static readonly IConverter<SampleClass> converter = ClassConverterBuilder.Create<SampleClass>()
.AddInt(o => o.num, (o,v) => o.num = v)
.AddString(o => o.str, (o,v) => o.str = v)
.ToConverter();
setter、getterと、対象フィールド用のプロセッサーを指定しています。
尚、フィールドの型によってはオプションの引数を指定する複数のオーバーロードを持つものもあります。
詳しくはAPIドキュメントを参照してください。
③ コンバーター/プロセッサーの出力
Add(Func<TTarget, TField> getter, Action<TTarget, TField> setter, IProcessor<TField> processor)
フィールドの型とメソッド名の対応は標準対応のデータ型一覧を参照してください。
対応するメソッドを持っていない型のフィールドの追加には、汎用Addメソッドを使用します。
汎用Addメソッドは概ね以下のような形式をとります。
最後にコンバーター又はプロセッサーを出力します。
コンバーターの出力にはToConverter()メソッドを、プロセッサーの出力にはToProcessor()メソッドを使用します。
◆ コンバーターの出力
ToConverter()メソッドを呼び出すと、ビルダーで構築した内容でコンバーターを出力します。
このコンバーターはバージョン管理されたバイトデータを扱うバージョン管理コンバーターの派生形です。
これは概ね、以下のような構造をとります。
バージョン管理コンバーター
バージョン管理プロセッサー
最新版プロセッサー
フィールド1用プロセッサー
フィールド2用プロセッサー
・
・
・
過去バージョン用
デシリアライザー
(コンバーターの出力時点では何も設定されない)
-
バージョン管理コンバーターは内部にバージョン管理プロセッサーを持っており、ほとんどの処理はバージョン管理プロセッサーに委譲されます。
-
バージョン管理プロセッサーは内部に最新版プロセッサーと複数の過去バージョン用デシリアライザーを持っており、シリアライズ処理と最新バージョンのByteデータのデシリアライズは最新版プロセッサーが、過去バージョンのByteデータのデシリアライズは過去バージョン用デシリアライザーが担当します。(ただし、バージョン管理コンバーター生成時点では過去バージョンのデシリアライザーは一つも設定されていません。詳しくは 9. バージョン管理コンバーター/プロセッサー を参照してください。)
-
最新版プロセッサーは対象オブジェクトのフィールド別にフィールド用プロセッサーを持っており、対象オブジェクトの各フィールドの変換処理は、このフィールド用プロセッサーに実行させます。
この中の「最新版プロセッサー」が、ToProcessor()で出力するプロセッサーと同じものになります。
尚、ToConverter()は省略可能な引数として、int型でバージョン番号を受け取ります。これは、最新版プロセッサーのバージョン番号として設定されます。省略した場合は0になります。
◆ プロセッサーの出力
ToProcessor()メソッドを呼び出すと、ビルダーで構築した内容でプロセッサーを出力します。
このプロセッサーはバージョン管理されていない基本のプロセッサーです。
尚、ビルダーによってはToConverter()、ToProcessor()以外の出力メソッドを持つものや、ビルダー特有の引数を必要とするものがあります。詳しくはビルダー別の解説と、APIドキュメントを参照してください。
アプリケーションのバージョンアップなどでプログラムに改修が入り、シリアライズ対象のデータ型が持つフィールドに追加や変更が加えられた場合の解決策は、バージョン管理コンバーターを使用することです。これにより、改修前のレイアウトで出力されたByteデータを、最新のレイアウトのオブジェクトに変換して読み込むことができます。
以下は、バージョン管理コンバーターを使ったプログラムのサンプルです。
9. バージョン管理コンバーター/プロセッサー
AddVersion(int versionNumber, IDeserializer<TTarget> deserializer)
バージョン管理コンバーターは、内部にバージョン管理プロセッサーを持っています。バージョン管理プロセッサーは、最新版のレイアウトを扱う1つのプロセッサーと、過去のレイアウトを扱う複数のデシリアライザーから構成されます。これらのプロセッサー/デシリアライザーは、バージョン管理プロセッサーに追加する際に指定するユニークなバージョン管理番号で識別され、Byteデータに書き込まれたバージョン管理番号を基準にデシリアライズ時に呼び出されるデシリアライザーが決定されます。シリアライズ時は常に最新版のレイアウトのプロセッサーが働きます。
サンプル全体を簡単に説明します。
L.5~11:最初のバージョンのクラス(FirstVersion)と、そのコンバーターを定義しています。
このコンバーターは、バージョン番号を指定せずに出力しています。よって、このコンバーターでシリアライズしたByteデータには、バージョン番号「0」が割り当てられます。
L.13~30:新しいバージョンのクラス(SecondVersion)と、そのコンバーターを定義しています。
L.20でバージョン番号「1」を指定してコンバーターを出力しています。
このコンバーターに対して、L.21~28で、旧バージョンのデシリアライザーを設定しています。
L.32~48:新旧それぞれのバージョンのデータを使った動作確認です。
L.36で作成した旧バージョンのByteデータと、L.41で作成した新バージョンのデータの両方を、L.43~44で、新バージョンのコンバーターを使用してデシリアライズしています。
L.21~28の、旧バージョンのデシリアライザーの設定についてもう少し詳しく説明します。
L.21で呼び出しているのはバージョン管理コンバーターが持つAddVersionメソッドです(ビルダーのメソッドではないことに注意してください)。このメソッドは、以下の形式をとります。
バージョン番号と、対応するデシリアライザーを指定しています。
プロセッサー(IProcessor<TTarget>)はデシリアライザー(IDeserializer<TTarget>)を継承していますので、プロセッサーを使用してバージョンを追加することも可能ですが、ここではFirstVersionのコンバーターが持つプロセッサーを使用するため、TemporaryDataDeserializer<TTarget, TWork>を使用して、FirstVersionのコンバーターが持つプロセッサーがデシリアライズしたFirstVersion型のオブジェクトを、SecondVersion型のオブジェクトに変換しています。
◆ TemporaryDataDeserializer<TTarget, TWork>
一度TWork型にデシリアライズしたオブジェクトを、TTarget型に変換してから出力するデシリアライザーです。
コンストラクターは以下のように定義されています。
第1引数でTWorkにデシリアライズするためのデシリアライザーを指定し、第2引数でTWorkからTTargetへ変換しています。
L22.では、TemporaryDataDeserializer<TTarget, TWork>のコンストラクターに渡すために、FirstVersionのコンバーターから、プロセッサーを取得しています。
以下の図は 8. ビルダーの基本 で紹介したバージョン管理コンバーターの構造図に、FirstVersionのコンバーターの状態を書き込んだものです。
TemporaryDataDeserializer(IDeserializer<TWork> deserializer, Func<TWork, TTarget> convertFunc)
バージョン管理コンバーター
(FirstVersion.converter)
バージョン管理プロセッサー
(FirstVersion.converter.Processor)
最新版プロセッサー
[バージョン番号:0]
(FirstVersion.converter.Processor.LatestVersion)
IntProcessor(num用)
過去バージョン用
デシリアライザー
(何も設定されていない)
FirstVersion.converterが、一番外側の「バージョン管理コンバーター」です。
FirstVersion.converter.Processorで、中段の「バージョン管理プロセッサー」が返されます。(コンバーターのProcessorプロパティは、そのコンバーターが持つプロセッサーを返します)
FirstVersion.converter.Processor.LatestVersionで、「最新版プロセッサー」が返されます。
これを追加したSecondVersion.converterは以下のような構造になります。
以上のように、バージョン管理コンバーターに対して古いバージョンのByteデータを処理できるデシリアライザーを追加することで、1つのコンバーターで複数のバージョンのデシリアライズ処理に対応することができるようになります。
バージョン管理コンバーター
(SecondVersion.converter)
バージョン管理プロセッサー
(SecondVersion.converter.Processor)
最新版プロセッサー
[バージョン番号:1]
(SecondVersion.converter.Processor.LatestVersion)
IntProcessor(num用)
StringProcessor(str用)
過去バージョン用デシリアライザー
[バージョン番号:0]
TemporaryDataDeserializer<SecondVersion, FirstVersion>
FirstVersion.converter.Processor.LatestVersion
から取得したプロセッサー
IntProcessor(num用)
シリアライズ対象のデータ型が多態性を持つ場合、多態性プロセッサーを用いることで、シリアライズされたインスタンスの型をデシリアライズ時に復元することができます。
以下は多態性プロセッサーを用いた処理のサンプルです。
10. 多態性プロセッサ―
多態性プロセッサーは同じ基底型を持つデータ型を対象とする複数のプロセッサーで構成されます。各プロセッサーは多態性コンバーターに追加する際に指定するユニークなIDで識別され、変換処理時の振り分けが行われます。
サンプル全体を簡単に説明します。
L.5~25:基底クラスの定義、基底クラスのプロセッサーの定義、多態性プロセッサー(を持ったコンバーター)の定義です。
L.20:L.21~23で作成している多態性プロセッサーを持ったコンバーターを作成しています。
L.21:多態性プロセッサーのインスタンスを作成しています。引数のByteOrderTypeは、IDを出力する際のバイト順の指定です。
L.22:BaseClassのプロセッサーをID=0で追加しています。
L.23:SubClassのプロセッサーをID=1で追加しています。
L.27~42:派生クラスの定義、派生クラスのプロセッサーの定義です。
L.44~57:基底クラスのインスタンスと派生クラスのインスタンスを使った動作確認です。
以上のように、基底型を対象とした多態性プロセッサーに、派生型(基底型そのものも含む)のプロセッサーを設定しておくことで、インスタンスの型を自動判定したシリアライズと、型を復元するデシリアライズが可能になります。
中間コンバータは、シリアライズ/デシリアライズの高速化と、処理負荷の分散を目的とした技術です。
このコンバータの処理対象のオブジェクトは、中間バイトデータと呼ばれるデータを持ちます。中間バイトデータは、対象オブジェクトのフィールドごとに用意されたメモリ領域で、フィールド値をバイトデータとして保持し、フィールドにアクセスされたタイミングでシリアライズ/デシリアライズが実行されます。これにより、オブジェクト全体のシリアライズ/デシリアライズ時の作業を減らし、処理速度の向上を図ります。
以下の図は通常のコンバーターと中間コンバーターのデシリアライズ時の動作を大まかに表したものです。
11. 中間コンバータ―
フィールド値A
フィールド値B
フィールド値C
③ フィールド値が要求されたらそのまま出力
② デシリアライズした対象オブジェクトを返す
① 全てのフィールドを一括デシリアライズして、対象オブジェクトの各フィールドに設定
Byteデータ
フィールド値A
通常のコンバーターのデシリアライズ
対象オブジェクト
コンバーター
フィールド値A
フィールド値B
フィールド値C
中間コンバーターのデシリアライズ
Byteデータ
中間ByteデータA
中間ByteデータB
中間ByteデータC
中間ByteデータA
中間ByteデータB
中間ByteデータC
コンバーター
対象オブジェクト
フィールド値A
① Byteデータをフィールドごとに振り分けて中間Byteデータとして保持させる
② 中間Byteデータを持ったオブジェクトを返す
③ 要求されたフィールド値だけデシリアライズして返す
このように、通常のコンバーターでは全てのフィールドを一括で変換するのに対し、中間コンバーターでは変換処理をフィールドにアクセスされたタイミングに分割します。
(シリアライズの処理は、概ねこの逆のフローになります)
このような処理を実現するために、中間コンバータの変換対象となるデータ型は、以下のように定義する必要があります。
-
対象データ型はIntermediateDataを継承する
-
対象データ型のフィールドはIField<TValue>型で定義する
-
対象データ型のインスタンスは対応する中間コンバーターを使って生成する
このような構造をとるため、中間コンバーターをビルダーで構築する際の記述方法は、通常のコンバーターとは若干異なります。
以下のサンプルをご覧ください。
このサンプルはSample01と同じ処理を中間コンバーターを使って書き直したものです。
Sample01との違いは以下の通りです。
L.4:対象クラスがIntermediateDataを継承している
L.5~6:対象クラスのフィールドがIField<TValue>になっている
L.10~11:Addメソッドの引数が異なる
通常のコンバーターでは、フィールド値の取得方法と設定方法を指定していましたが、中間コンバーターではIField<TValue>型の変数に対して、中間フィールドオブジェクトを設定する方法を指定します。
L.17:対象クラスのインスタンスをコンバーターで生成している
L.18~19, 24:フィールドへの値の設定、取得はIField<TValue>が持つ、Valueプロパティを通して行われる。
以上のように、通常のコンバーターと比べて使用する上での決まりごとが多いですが、適切に使用することで大幅にパフォーマンスアップできる場合があります。
12. クラスコンバーター/プロセッサーの機能と構築方法
クラスコンバーター/プロセッサーはclass型(参照型)の複合データ型をシリアライズ/デシリアライズするための機能で、ClassConverterBuilderで構築します。
以下はClassConverterBuilderを使ったクラスコンバーターの構築サンプルです。
Add型名(Func<TTarget, TField> getter, Action<TTarget, TField> setter)
-
Create<TTarget>(Func<TTarget> createInstance, ByteOrderType byteOrderType, ClassConverterMode mode)
-
Create<TTarget>(Func<TTarget> createInstance, ByteOrderType byteOrderType)
-
Create<TTarget>(Func<TTarget> createInstance)
-
Create<TTarget>(ByteOrderType byteOrderType, ClassConverterMode mode)
-
Create<TTarget>(ByteOrderType byteOrderType)
-
Create<TTarget>()
Addメソッドは以下のような形式となります。(型ごとの実際のメソッド名は、標準対応のデータ型一覧を参照してください)
◆ ビルダーの解説
Createメソッドには以下のオーバーロードがあります。
IClassSymmetricProcessor<TTarget> ToProcessor()
プロセッサーは以下のメソッドで出力します。
このメソッドが出力するコンバーターはバージョン管理コンバーターの派生形です。
引数でバージョン番号を指定することが可能で、引数を省略した場合のバージョン番号は0になります。
IVersioningClassSymmetricConverter<TTarget> ToConverter(int versionNumber)
コンバーターは以下のメソッドで出力します。
-
IConverter<TTarget>
-
IClassConverter<TTarget>
-
IVersioningConverter<TTarget>
-
IVersioningClassConverter<TTarget>
-
IClassSymmetricConverter<TTarget>
ClassConverterBuilderで構築したコンバーターはIVersioningClassSymmetricConverter<TTarget>インターフェイスで出力されます。
これは以下のインターフェイスを継承しています。
◆ コンバーターの解説
IVersioningClassSymmetricConverter<TTarget>は以下のプロパティ、メソッドを持っています。
・IVersioningClassSymmetricProcessor<TTarget> Processor
このコンバーターの変換処理を担うプロセッサーを返します。
・IVersioningClassSymmetricConverter<TTarget> AddVersion(int versionNumber, IDeserializer<TTarget> deserializer)
このコンバーターに別のバージョンのデシリアライザ―を追加します。
第1引数:バージョン番号。
第2引数:追加するデシリアライザ―。
戻り値:呼び出したオブジェクト(メソッドチェイン)。
・byte[] Serialize(TTarget target)
シリアライズを実行します。
第1引数:シリアライズ対象のオブジェクト。
戻り値:シリアライズ結果のbyte配列。
・byte[] SerializeWithoutTrimming(TTarget target)
シリアライズを実行します。ただし、結果をトリムしません。
第1引数:シリアライズ対象のオブジェクト。
戻り値:シリアライズ結果のbyte配列。
・TTarget Deserialize(byte[] bytes)
デシリアライズを実行します。
第1引数:シリアライズデータが格納されたbyte配列。
戻り値:デシリアライズされたオブジェクト。
・TTarget Deserialize(TTarget value, byte[] bytes)
インスタンスを再利用してデシリアライズを実行します。
第1引数:再利用するインスタンス。
第2引数:シリアライズデータが格納されたbyte配列。
戻り値:デシリアライズされたオブジェクト。
※ トリムについて
Byteprocが標準で用意しているプロセッサーの中には、シリアライズの準備段階でbyte配列を作成する際に、本来シリアライズに必要なサイズよりも大きいサイズを要求するものがあります。
この方法は、正確なサイズを取得しようとすると時間がかかるデータ型において、大まかでかつ必要十分なサイズを確保することで、実行時間を短縮できる場合に採用されています。
このようなデータ型が含まれていると、シリアライズ後のbyte配列に余白が発生する場合がありますが、通常のシリアライズではこの余白をトリムしたbyte配列を返しています。
SerializeWithoutTrimmingを使用すると結果をトリムせずに返すため、返されるbyte配列に余分なサイズが含まれる場合がありますが、トリムのためにかかるコストを節約することができます。
※ インスタンスの再利用について
通常のデシリアライズ処理では、処理の中で出力対象のデータ型のインスタンスを作成し、そのインスタンスのフィールドに対して、デシリアライズした値を格納していきますが、Deserializeメソッドの引数に出力対象のデータ型のインスタンスを設定すると、設定したインスタンスのフィールドに対してデシリアライズした値を格納して返します。
この機能は、あるインスタンスを使い続けたい場合や、インスタンスの生成に時間がかかる場合に実行時間短縮の目的で使用したりします。
インスタンスの再利用は対象データ型がclass(参照型)の場合のみ使用可能で、構造体(値型)の場合は使用できません。
-
IClassProcessor<TTarget>
-
IReusableValueProcessor<TTarget>
-
IProcessor<TTarget>
-
IReusableDeserializer<TTarget>
-
IDeserializer<TTarget>
ClassConverterBuilderで構築したプロセッサーはIClassSymmetricProcessor<TTarget>インターフェイスで出力されます。
これは以下のインターフェイスを継承しています。
◆ プロセッサーの解説
IClassSymmetricProcessor<TTarget>は以下のプロパティ、メソッドを持っています。
・int GetByteSize(TTarget target)
引数に指定されたオブジェクトをシリアライズしたときのサイズを返します。
第1引数:サイズを取得するオブジェクト。
戻り値:サイズ
・void Serialize(byte[] bytes, ref int offset, TTarget target)
シリアライズを実行します。
第1引数:シリアライズ後のデータが設定されます。シリアライズに必要なサイズのbyte配列を作成して渡す必要があります。
第2引数:シリアライズしたデータの格納開始位置を指定します。実行後はシリアライズ終了位置+1が格納されます。
第3引数:シリアライズ対象のオブジェクトを指定します。
・int GetByteSize(byte[] bytes, int offset)
このプロセッサーの対象データのサイズを返します。
第1引数:シリアライズデータが格納されたbyte配列。
第2引数:このプロセッサーの対象データの開始位置。
戻り値:サイズ
・TTarget Deserialize(byte[] bytes, ref int offset)
デシリアライズを実行します。
第1引数:シリアライズデータが格納されたbyte配列。
第2引数:このプロセッサーの対象データの開始位置。実行後はデシリアライズ終了位置+1が格納されます。
戻り値:デシリアライズされたオブジェクト。
・TTarget Deserialize(TTarget target, byte[] bytes, ref int offset)
インスタンスを再利用してデシリアライズを実行します。
第1引数:再利用するインスタンス。
第2引数:シリアライズデータが格納されたbyte配列。
第3引数:このプロセッサーの対象データの開始位置。実行後はデシリアライズ終了位置+1が格納されます。
戻り値:デシリアライズされたオブジェクト。
・SubClessParameter<TTargetSub> CreateSubClassParameter<TTargetSub>()
このプロセッサーが持つ、対象フィールドの情報をSubClessParameterに格納して返します。
これは、このプロセッサーが処理対象とするクラスを継承したクラスのプロセッサーを作成する際にビルダーのAddBaseClassParameterに指定することで、このプロセッサーが持つ対象フィールドの情報を引き継ぐことができます。
総称型引数:引き継ぎ先のプロセッサーが処理対象とするデータ型。(このプロセッサーが対象とするデータ型の派生型のみ指定可)
戻り値:このプロセッサーが持つ対象フィールドの情報
注意:この機能はiOS環境で正常に動作しない場合があります。詳しくは「Note 1. iOS環境の制限」を参照してください
※ CreateSubClassParameterを使ったフィールド情報の引継ぎについて
以下はフィールド情報引継ぎのサンプルです。
L.21でInheritSampleBaseのプロセッサーのCreateSubClassParameterメソッドの戻り値を、ビルダーのAddBaseClassParameterメソッドに渡しています。
このスクリプトをUnityエディタ上で適当なゲームオブジェクトにセットして実行ボタンを押すと、コンソールに「num = 123, str = abc, num2 = 456」と表示され、InheritSampleBaseのプロセッサーからInheritSampleSubのコンバーターへフィールド情報が引き継がれていることが確認できます。
構造体コンバーター/プロセッサーはstruct型(値型)の複合データ型をシリアライズ/デシリアライズするための機能で、StructConverterBuilderで構築します。
以下はStructConverterBuilderを使った構造体コンバーターの構築サンプルです。
13. 構造体コンバーター/プロセッサーの機能と構築方法
IVersioningConverter<TTarget> ToConverter(int versionNumber)
このメソッドが出力するコンバーターはバージョン管理コンバーターです。
引数でバージョン番号を指定することが可能で、引数を省略した場合のバージョン番号は0になります。
プロセッサーは以下のメソッドで出力します。
IProcessor<TTarget> ToProcessor()
Addメソッドは以下のような形式となります。(型ごとの実際のメソッド名は、標準対応のデータ型一覧を参照してください)
-
Create<TTarget>(Func<TTarget> createInstance, ByteOrderType byteOrderType, StructConverterMode mode)
-
Create<TTarget>(Func<TTarget> createInstance, ByteOrderType byteOrderType)
-
Create<TTarget>(Func<TTarget> createInstance)
-
Create<TTarget>(ByteOrderType byteOrderType, StructConverterMode mode)
-
Create<TTarget>(ByteOrderType byteOrderType)
-
Create<TTarget>()
Add型名(StructFieldGetter<TTarget, TField> getter, StructFieldSetter<TTarget, TField> setter)
コンバーターは以下のメソッドで出力します。
Createメソッドには以下のオーバーロードがあります。
◆ ビルダーの解説
IProcessor<TTarget>は以下のプロパティ、メソッドを持っています。
・int GetByteSize(TTarget target)
引数に指定されたオブジェクトをシリアライズしたときのサイズを返します。
第1引数:サイズを取得するオブジェクト。
戻り値:サイズ
・void Serialize(byte[] bytes, ref int offset, TTarget target)
シリアライズを実行します。
第1引数:シリアライズ後のデータが設定されます。シリアライズに必要なサイズのbyte配列を作成して渡す必要があります。
第2引数:シリアライズしたデータの格納開始位置を指定します。実行後はシリアライズ終了位置+1が格納されます。
第3引数:シリアライズ対象のオブジェクトを指定します。
・int GetByteSize(byte[] bytes, int offset)
このプロセッサーの対象データのサイズを返します。
第1引数:シリアライズデータが格納されたbyte配列。
第2引数:このプロセッサーの対象データの開始位置。
戻り値:サイズ
・TTarget Deserialize(byte[] bytes, ref int offset)
デシリアライズを実行します。
第1引数:シリアライズデータが格納されたbyte配列。
第2引数:このプロセッサーの対象データの開始位置。実行後はデシリアライズ終了位置+1が格納されます。
戻り値:デシリアライズされたオブジェクト。
-
IDeserializer<TTarget>
StructConverterBuilderで構築したプロセッサーはIProcessor<TTarget>インターフェイスで出力されます。
これは以下のインターフェイスを継承しています。
◆ プロセッサーの解説
IVersioningConverter<TTarget>は以下のプロパティ、メソッドを持っています。
・IVersioningProcessor<TTarget> Processor
このコンバーターの変換処理を担うプロセッサーを返します。
・IVersioningConverter<TTarget> AddVersion(int versionNumber, IDeserializer<TTarget> deserializer)
このコンバーターに別のバージョンのデシリアライザ―を追加します。
第1引数:バージョン番号。
第2引数:追加するデシリアライザ―。
戻り値:呼び出したオブジェクト(メソッドチェイン)。
・byte[] Serialize(TTarget target)
シリアライズを実行します。
第1引数:シリアライズ対象のオブジェクト。
戻り値:シリアライズ結果のbyte配列。
・byte[] SerializeWithoutTrimming(TTarget target)
シリアライズを実行します。ただし、結果をトリムしません。
第1引数:シリアライズ対象のオブジェクト。
戻り値:シリアライズ結果のbyte配列。
・TTarget Deserialize(byte[] bytes)
デシリアライズを実行します。
第1引数:シリアライズデータが格納されたbyte配列。
戻り値:デシリアライズされたオブジェクト。
◆ コンバーターの解説
StructConverterBuilderで構築したコンバーターはIVersioningConverter<TTarget>インターフェイスで出力されます。
これは以下のインターフェイスを継承しています。
-
IConverter<TTarget>
クラス非対称コンバーター/プロセッサーはclass型(参照型)の複合データ型をシリアライズ/デシリアライズするための機能で、ClassAsymmetricConverterBuilderで構築します。
非対称コンバーターのデシリアライズは、一旦ワークオブジェクトを作成し、ワークオブジェクトから目的のオブジェクトを作成します。そのため、コンストラクタでしか設定できない値がある場合や、readonly指定された値がある場合、複雑な計算によって求めた値をセットしたい場合などに、対象オブジェクトの設計を変えることなく使用することができます。
非対称コンバーターのシリアライズは、通常のコンバーターと同じ処理になります。
以下はClassAsymmetricConverterBuilderを使ったクラス非対称コンバーターの構築サンプルです。
14. クラス非対称コンバーター/プロセッサーの機能と構築方法
IVersioningClassAsymmetricConverterは以下のプロパティ、メソッドを持っています。
・IVersioningClassAsymmetricProcessor<TTarget, TWork> Processor
このコンバーターの変換処理を担うプロセッサーを返します。
・IVersioningClassAsymmetricConverter<TTarget, TWork> AddVersion(int versionNumber, IDeserializer<TTarget> deserializer)
このコンバーターに別のバージョンのデシリアライザ―を追加します。
第1引数:バージョン番号。
第2引数:追加するデシリアライザ―。
戻り値:呼び出したオブジェクト(メソッドチェイン)。
・byte[] Serialize(TTarget target)
シリアライズを実行します。
第1引数:シリアライズ対象のオブジェクト。
戻り値:シリアライズ結果のbyte配列。
・byte[] SerializeWithoutTrimming(TTarget target)
シリアライズを実行します。ただし、結果をトリムしません。
第1引数:シリアライズ対象のオブジェクト。
戻り値:シリアライズ結果のbyte配列。
・TTarget Deserialize(byte[] bytes)
デシリアライズを実行します。
第1引数:シリアライズデータが格納されたbyte配列。
戻り値:デシリアライズされたオブジェクト。
・TTarget Deserialize(TTarget value, byte[] bytes)
インスタンスを再利用してデシリアライズを実行します。
第1引数:再利用するインスタンス。
第2引数:シリアライズデータが格納されたbyte配列。
戻り値:デシリアライズされたオブジェクト。
◆ コンバーターの解説
ClassAsymmetricConverterBuilderで構築したコンバーターはIVersioningClassAsymmetricConverter<TTarget, TWork>インターフェイスで出力されます。
これは以下のインターフェイスを継承しています。(直接継承しているのはIVersioningClassConverter<TTarget>とIClassAsymmetricConverter<TTarget, TWork>です)
-
IConverter<TTarget>
-
IClassConverter<TTarget>
-
IVersioningConverter<TTarget>
-
IVersioningClassConverter<TTarget>
-
IClassAsymmetricConverter<TTarget, TWork>
IClassAsymmetricProcessor<TTarget, TWork>は以下のプロパティ、メソッドを持っています。
・int GetByteSize(TTarget target)
引数に指定されたオブジェクトをシリアライズしたときのサイズを返します。
第1引数:サイズを取得するオブジェクト。
戻り値:サイズ
・void Serialize(byte[] bytes, ref int offset, TTarget target)
シリアライズを実行します。
第1引数:シリアライズ後のデータが設定されます。シリアライズに必要なサイズのbyte配列を作成して渡す必要があります。
第2引数:シリアライズしたデータの格納開始位置を指定します。実行後はシリアライズ終了位置+1が格納されます。
第3引数:シリアライズ対象のオブジェクトを指定します。
・int GetByteSize(byte[] bytes, int offset)
このプロセッサーの対象データのサイズを返します。
第1引数:シリアライズデータが格納されたbyte配列。
第2引数:このプロセッサーの対象データの開始位置。
戻り値:サイズ
・TTarget Deserialize(byte[] bytes, ref int offset)
デシリアライズを実行します。
第1引数:シリアライズデータが格納されたbyte配列。
第2引数:このプロセッサーの対象データの開始位置。実行後はデシリアライズ終了位置+1が格納されます。
戻り値:デシリアライズされたオブジェクト。
・TTarget Deserialize(TTarget target, byte[] bytes, ref int offset)
インスタンスを再利用してデシリアライズを実行します。
第1引数:再利用するインスタンス。
第2引数:シリアライズデータが格納されたbyte配列。
第3引数:このプロセッサーの対象データの開始位置。実行後はデシリアライズ終了位置+1が格納されます。
戻り値:デシリアライズされたオブジェクト。
・SubClessAsymmetricParameter<TTargetSub, TWorkSub> CreateSubClassParameter<TTargetSub, TWorkSub>()
このプロセッサーが持つ、対象フィールドの情報をSubClessParameterに格納して返します。
これは、このプロセッサーが処理対象とするクラスを継承したクラスのプロセッサーを作成する際にビルダーのAddBaseClassParameterに指定することで、このプロセッサーが持つ対象フィールドの情報を引き継ぐことができます。
TTargetSub:引き継ぎ先のプロセッサーが処理対象とするデータ型。(このプロセッサーが対象とするデータ型の派生型のみ指定可)
TWorkSub:引き継ぎ先のプロセッサーが使用するワークオブジェクトのデータ型。(このプロセッサーが使用するワークオブジェクトのデータ型の派生型のみ指定可)
戻り値:このプロセッサーが持つ対象フィールドの情報
注意:この機能はiOS環境で正常に動作しない場合があります。詳しくは「Note 1. iOS環境の制限」を参照してください
-
IClassProcessor<TTarget>
-
IReusableValueProcessor<TTarget>
-
IProcessor<TTarget>
-
IReusableDeserializer<TTarget>
-
IDeserializer<TTarget>
ClassAsymmetricConverterBuilderで構築したプロセッサーはIClassAsymmetricProcessor<TTarget, TWork>インターフェイスで出力されます。
これは以下のインターフェイスを継承しています。(直接継承しているのはIClassProcessor<TTarget>です)
◆ プロセッサーの解説
IClassAsymmetricProcessor<TTarget, TWork> ToProcessor(Func<TTarget, TWork, TTarget> createInstance)
引数のデリゲートで再利用オブジェクトとワークオブジェクトから目的のオブジェクトを作成する処理を指定します。
このデリゲートは通常のデシリアライズと、インスタンスを再利用するデシリアライズの両方で使用されます。通常のデシリアライズから呼び出された場合、再利用オブジェクトにはnullが設定されます。
引数のデリゲートでワークオブジェクトから目的のオブジェクトを作成する処理を指定します。
このデリゲートは通常のデシリアライズと、インスタンスを再利用するデシリアライズの両方で使用されます。インスタンスを再利用するデシリアライズを実行した場合も、再利用オブジェクトが使用されることはありません。
IVersioningClassAsymmetricConverter<TTarget, TWork> ToConverter(Func<TTarget, TWork, TTarget> createInstance, int versionNumber)
第1引数:再利用オブジェクトとワークオブジェクトから目的のオブジェクトを作成する処理を指定します。
第2引数:バージョン番号を指定することが可能です。引数を省略した場合のバージョン番号は0になります。
この第1引数に指定するデリゲートは、通常のデシリアライズと、インスタンスを再利用するデシリアライズの両方で使用されます。通常のデシリアライズから呼び出された場合、再利用オブジェクトにはnullが設定されます。
IClassAsymmetricProcessor<TTarget, TWork> ToProcessor(Func<TWork, TTarget> createInstance)
プロセッサーは以下のいずれかのメソッドで出力します。
第1引数:ワークオブジェクトから目的のオブジェクトを作成する処理を指定します。
第2引数:バージョン番号を指定することが可能です。引数を省略した場合のバージョン番号は0になります。
この第1引数に指定するデリゲートは、通常のデシリアライズと、インスタンスを再利用するデシリアライズの両方で使用されます。インスタンスを再利用するデシリアライズを実行した場合も、再利用オブジェクトが使用されることはありません。
IVersioningClassAsymmetricConverter<TTarget, TWork> ToConverter(Func<TWork, TTarget> createInstance, int versionNumber)
コンバーターは以下のいずれかのメソッドで出力します。
Add型名(Func<TTarget, TField> getter, Action<TWork, TField> setter)
Addメソッドは以下のような形式となります。(型ごとの実際のメソッド名は、標準対応のデータ型一覧を参照してください)
-
Create<TTarget, TWork>(ByteOrderType byteOrderType, ClassAsymmetricConverterMode mode)
-
Create<TTarget, TWork>(ByteOrderType byteOrderType)
-
Create<TTarget, TWork>()
Createメソッドには以下のオーバーロードがあります。
◆ ビルダーの解説
15. 構造体非対称コンバーター/プロセッサーの機能と構築方法
構造体非対称コンバーター/プロセッサーはstruct型(値型)の複合データ型をシリアライズ/デシリアライズするための機能で、StructAsymmetricConverterBuilderで構築します。
非対称コンバーターのデシリアライズは、一旦ワークオブジェクトを作成し、ワークオブジェクトから目的のオブジェクトを作成します。そのため、コンストラクタでしか設定できない値がある場合や、readonly指定された値がある場合、複雑な計算によって求めた値をセットしたい場合などに、対象オブジェクトの設計を変えることなく使用することができます。
非対称コンバーターのシリアライズは、通常のコンバーターと同じ処理になります。
以下はStructAsymmetricConverterBuilderを使った構造体非対称コンバーターの構築サンプルです。
引数のデリゲートでワークオブジェクトから目的のオブジェクトを作成する処理を指定します。
IProcessor<TTarget> ToProcessor(Func<TWork, TTarget> createInstance)
プロセッサーは以下のメソッドで出力します。
第1引数:ワークオブジェクトから目的のオブジェクトを作成する処理を指定します。
第2引数:バージョン番号を指定することが可能です。引数を省略した場合のバージョン番号は0になります。
IVersioningConverter<TTarget> ToConverter(Func<TWork, TTarget> createInstance, int versionNumber)
コンバーターは以下のメソッドで出力します。
Add型名(Func<TTarget, TField> getter, Action<TWork, TField> setter)
Addメソッドは以下のような形式となります。(型ごとの実際のメソッド名は、標準対応のデータ型一覧を参照してください)
-
Create<TTarget, TWork>(ByteOrderType byteOrderType, StructAsymmetricConverterMode mode)
-
Create<TTarget, TWork>(ByteOrderType byteOrderType)
-
Create<TTarget, TWork>()
Createメソッドには以下のオーバーロードがあります。
◆ ビルダーの解説
IVersioningConverter<TTarget>は以下のプロパティ、メソッドを持っています。
・IVersioningProcessor<TTarget> Processor
このコンバーターの変換処理を担うプロセッサーを返します。
・IVersioningConverter<TTarget> AddVersion(int versionNumber, IDeserializer<TTarget> deserializer)
このコンバーターに別のバージョンのデシリアライザ―を追加します。
第1引数:バージョン番号。
第2引数:追加するデシリアライザ―。
戻り値:呼び出したオブジェクト(メソッドチェイン)。
・byte[] Serialize(TTarget target)
シリアライズを実行します。
第1引数:シリアライズ対象のオブジェクト。
戻り値:シリアライズ結果のbyte配列。
・byte[] SerializeWithoutTrimming(TTarget target)
シリアライズを実行します。ただし、結果をトリムしません。
第1引数:シリアライズ対象のオブジェクト。
戻り値:シリアライズ結果のbyte配列。
・TTarget Deserialize(byte[] bytes)
デシリアライズを実行します。
第1引数:シリアライズデータが格納されたbyte配列。
戻り値:デシリアライズされたオブジェクト。
-
IConverter<TTarget>
StructAsymmetricConverterBuilderで構築したコンバーターはIVersioningConverter<TTarget>インターフェイスで出力されます。
これは以下のインターフェイスを継承しています。
◆ コンバーターの解説
-
IDeserializer<TTarget>
StructAsymmetricConverterBuilderで構築したプロセッサーはIProcessor<TTarget>インターフェイスで出力されます。
これは以下のインターフェイスを継承しています。
◆ プロセッサーの解説
IProcessor<TTarget>は以下のプロパティ、メソッドを持っています。
・int GetByteSize(TTarget target)
引数に指定されたオブジェクトをシリアライズしたときのサイズを返します。
第1引数:サイズを取得するオブジェクト。
戻り値:サイズ
・void Serialize(byte[] bytes, ref int offset, TTarget target)
シリアライズを実行します。
第1引数:シリアライズ後のデータが設定されます。シリアライズに必要なサイズのbyte配列を作成して渡す必要があります。
第2引数:シリアライズしたデータの格納開始位置を指定します。実行後はシリアライズ終了位置+1が格納されます。
第3引数:シリアライズ対象のオブジェクトを指定します。
・int GetByteSize(byte[] bytes, int offset)
このプロセッサーの対象データのサイズを返します。
第1引数:シリアライズデータが格納されたbyte配列。
第2引数:このプロセッサーの対象データの開始位置。
戻り値:サイズ
・TTarget Deserialize(byte[] bytes, ref int offset)
デシリアライズを実行します。
第1引数:シリアライズデータが格納されたbyte配列。
第2引数:このプロセッサーの対象データの開始位置。実行後はデシリアライズ終了位置+1が格納されます。
戻り値:デシリアライズされたオブジェクト。
Note 1. iOS環境の制限
現状で、これらの機能を回避して同様の結果を得るための最も簡単な方法は、基底クラスのフィールドのスコープをprotectedかpublicにし、派生クラスのプロセッサを構築する際に、基底クラスのフィールドを含む全てのフィールドを追加することです。
ByteClapライブラリの対策、改修については、現在検討中です。
お手数ですが、対策が完了するまでは上記のような方法で回避してくださいますよう、お願い致します。