私は自分のC#/。NETゲームでこのアプローチをよく使用します。ここで説明する他の利点(および危険!)の他に、シリアル化の問題を回避するのにも役立ちます。
.NET Frameworkの組み込みバイナリシリアル化機能を利用する場合は、エンティティIDを使用すると、書き出されるオブジェクトグラフのサイズを最小限に抑えることができます。デフォルトでは、.NETのバイナリフォーマッタは、オブジェクトグラフ全体をフィールドレベルでシリアル化します。シリアル化したいとしましょうShip
インスタンス。所有者を参照Ship
する_owner
フィールドがある場合Player
、そのPlayer
インスタンスもシリアル化されます。場合にPlayer
含まれている_ships
(と言うのフィールドをICollection<Ship>
)、その後、すべてのプレイヤーの船のも、フィールドレベル(再帰的)で参照される他のオブジェクトとともに、書き出されます。巨大なオブジェクトグラフの一部だけをシリアル化したい場合、誤って巨大なオブジェクトグラフをシリアル化するのは簡単です。
代わりに、_ownerId
フィールドがある場合は、その値を使用してPlayer
参照をオンデマンドで解決できます。私のパブリックAPIは変更しないままにすることもでき、Owner
プロパティは単にルックアップを実行します。
ハッシュベースのルックアップは一般に非常に高速ですが、ルックアップが頻繁に行われる非常に大規模なエンティティセットでは、追加されたオーバーヘッドが問題になる可能性があります。問題が発生した場合は、シリアル化されないフィールドを使用して参照をキャッシュできます。たとえば、次のようなことができます。
public class Ship
{
private int _ownerId;
[NonSerialized] private Lazy<Player> _owner;
public Player Owner
{
get { return _owner.Value; }
}
public Ship(Player owner)
{
_ownerId = owner.PlayerID;
EnsureCache();
}
private void EnsureCache()
{
if (_owner == null)
_owner = new Lazy<Player>(() => Game.Current.Players[_ownerId]);
}
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
EnsureCache();
}
}
もちろん、このアプローチはシリアル化を難しくすることもできます実際に大きなオブジェクトグラフをシリアル化する必要がある場合にます。そのような場合、必要なオブジェクトがすべて含まれるように、何らかの「コンテナ」を考案する必要があります。