C#ゲームでBinaryFormatterを使用して、ユーザーのゲームの進行状況やゲームレベルなどを保存します。下位互換性の問題が発生しています。
目的:
- レベルデザイナーがキャンペーン(レベルとルール)を作成し、コードを変更しても、キャンペーンは正常に機能するはずです。これは、リリース前の開発中に毎日発生する可能性があります。
- ユーザーがゲームを保存します。ゲームパッチをリリースします。ユーザーは引き続きゲームをロードできるはずです。
- 目に見えないデータ変換プロセスは、2つのバージョンがどれほど離れていても機能するはずです。たとえば、ユーザーは最初の5つのマイナーアップデートをスキップして、6番目のアップデートを直接取得できます。それでも、彼の保存したゲームはまだ正常にロードされるはずです。
ソリューションは、ユーザーやレベルデザイナーには完全に見えないようにする必要があり、何かを変更したいコーダーに最小限の負担をかける必要があります(たとえば、より良い名前を考えたためにフィールドの名前を変更します)。
シリアル化するオブジェクトグラフの中には、あるクラスに根ざしているものもあれば、他のクラスに根ざしているものもあります。上位互換性は必要ありません。
変更を壊す可能性があります(古いバージョンをシリアル化し、新しいバージョンに逆シリアル化するとどうなりますか):
- フィールドを追加します(デフォルトで初期化されます)
- フィールドタイプの変更(失敗)
- フィールドの名前を変更します(フィールドを削除して新しいフィールドを追加するのと同じです)
- プロパティをフィールドに変更して元に戻します(名前の変更に相当)
- 自動実装プロパティを変更して、バッキングフィールドを使用するようにします(名前の変更に相当)
- スーパークラスを追加します(現在のクラスにフィールドを追加するのと同じです)
- フィールドを別の方法で解釈します(たとえば、度単位、現在はラジアン単位)
- ISerializableを実装する型の場合、ISerializableメソッドの実装を変更する場合があります(たとえば、非常に大きな型のISerializable実装内で圧縮の使用を開始します)
- クラスの名前を変更し、列挙値の名前を変更します
私はについて読んだ:
- バージョントレラントなシリアル化
- IDeserializationCallback
- [OptionalField(VersionAdded)]
- [OnDeserializing]、[OnDeserialized]、[OnSerializing]、[OnSerialized]。
- [シリアル化されていません]
私の現在の解決策:
- OnDeserializingコールバックなどを使用して、可能な限り多くの変更を中断せずに行います。
- 重大な変更は2週間に1回の予定であるため、維持する互換性コードは少なくなります。
- 重大な変更を加える前に、使用するすべての[Serializable]クラスを、OldClassVersions.VersionX(Xは最後の序数の次の序数)という名前空間/フォルダーにコピーします。すぐにリリースする予定がない場合でも、これを行います。
- ファイルに書き込む場合、シリアル化するのはこのクラスのインスタンスです。class SaveFileData {int version; オブジェクトデータ; }
- ファイルから読み取るとき、SaveFileDataを逆シリアル化し、次のようなことを行う反復的な「更新」ルーチンに渡します。
。
for(int i = loadedData.version; i < CurrentVersion; i++)
{
// Update() takes an instance of OldVersions.VersionX.TheClass
// and returns an instance of OldVersions.VersionXPlus1.TheClass
loadedData.data = Update(loadedData.data, i);
}
- 便宜上、Update()関数は、その実装で、リフレクションを使用して古いバージョンから新しいバージョンにできるだけ多くのデータをコピーするCopyOverlappingPart()関数を使用できます。このように、Update()関数は、実際に変更されたもののみを処理できます。
それに関するいくつかの問題:
- デシリアライザーは、クラスOldClassVersions.Version5.FooではなくクラスFooにデシリアライズします。これは、クラスFooがシリアル化されたものであるためです。
- テストやデバッグはほとんど不可能です
- 多くのクラスの古いコピーを保持する必要がありますが、これはエラーが発生しやすく、壊れやすく、煩わしいものです。
- クラスの名前を変更したいときにどうしたらよいかわかりません
これは本当に一般的な問題です。人々は通常どのようにそれを解決しますか?