First of all
I do not know any distributed system, and I do not pretend to create it. This post explains how you can model this behavior with .NET and C # using the IObjectReference interface with serializable objects.
Now let's continue the show
, .NET IObjectReference . ISerializable.GetObjectData SerializationInfo.SetType -, IObjectReference ( , GetObjectData) , .
:
[Serializable]
internal sealed class SerializationProxy<TOwner, TKey> : ISerializable, IObjectReference {
private const string KeyName = "Key";
private const string InstantiatorName = "Instantiator";
private static readonly Type thisType = typeof(SerializationProxy<TOwner, TKey>);
private static readonly Type keyType = typeof(TKey);
private static readonly Type instantiatorType = typeof(Func<TKey, TOwner>);
private readonly Func<TKey, TOwner> _instantiator;
private readonly TKey _key;
private SerializationProxy() {
}
private SerializationProxy(SerializationInfo info, StreamingContext context) {
if (info == null) throw new ArgumentNullException("info");
_key = (TKey)info.GetValue(KeyName, keyType);
_instantiator = (Func<TKey, TOwner>)info.GetValue(InstantiatorName, instantiatorType);
}
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) {
throw new NotSupportedException("This type should never be serialized.");
}
object IObjectReference.GetRealObject(StreamingContext context) {
return _instantiator(_key);
}
internal static void PrepareSerialization(SerializationInfo info, TKey key, Func<TKey, TOwner> instantiator) {
if (info == null) throw new ArgumentNullException("info");
if (instantiator == null) throw new ArgumentNullException("instantiator");
info.SetType(thisType);
info.AddValue(KeyName, key, keyType);
info.AddValue(InstantiatorName, instantiator, instantiatorType);
}
}
SerializationProxy.PrepareSerialization(info, myKey, myKey = > LoadedInstances.GetById(myKey)) GetObjectData, LoadedInstances.GetById < TKey, WeakReference > / , .
EDIT:
, , .
public static class Program {
public static void Main() {
var item = new Item { Name = "Bleh" };
var bytes = Serialize(item);
{
var loadedItem1 = Deserialize<Item>(bytes);
var loadedItem2 = Deserialize<Item>(bytes);
Debug.Assert(loadedItem1.Id == loadedItem2.Id);
Debug.Assert(loadedItem1.Name == loadedItem2.Name);
Debug.Assert(ReferenceEquals(loadedItem1, loadedItem2));
loadedItem1.Name = "Bluh";
Debug.Assert(loadedItem1.Name == loadedItem2.Name);
}
{
var loadedItem1 = Deserialize<Item>(bytes);
Debug.Assert(loadedItem1.Name == "Bluh");
loadedItem1.Name = "Blargh";
var loadedItem2 = Deserialize<Item>(bytes);
Debug.Assert(loadedItem1.Name == loadedItem2.Name);
}
}
#region Serialization helpers
private static readonly IFormatter _formatter
= new BinaryFormatter();
public static byte[] Serialize(ISerializable item) {
using (var stream = new MemoryStream()) {
_formatter.Serialize(stream, item);
return stream.ToArray();
}
}
public static T Deserialize<T>(Byte[] bytes) {
using (var stream = new MemoryStream(bytes)) {
return (T)_formatter.Deserialize(stream);
}
}
#endregion
}
public interface IDomainObject {
Guid Id { get; }
}
public static class LoadedInstances<T>
where T : class, IDomainObject {
private static readonly Dictionary<Guid, WeakReference> _items
= new Dictionary<Guid, WeakReference>();
public static void Set(T item) {
var itemId = item.Id;
if (_items.ContainsKey(itemId))
_items.Remove(itemId);
_items.Add(itemId, new WeakReference(item));
}
public static T Get(Guid id) {
if (_items.ContainsKey(id)) {
var itemRef = _items[id];
return (T)itemRef.Target;
}
return null;
}
}
[DebuggerDisplay("{Id} {Name}")]
[Serializable]
public class Item : IDomainObject, ISerializable {
public Guid Id { get; private set; }
public String Name { get; set; }
public Item() {
Id = Guid.NewGuid();
LoadedInstances<Item>.Set(this);
}
#region ISerializable Members
public void GetObjectData(SerializationInfo info, StreamingContext context) {
SerializationProxy<Item, Guid>.PrepareSerialization(info, Id, GetById);
}
#endregion
public static Item GetById(Guid id) {
var alreadyLoaded = LoadedInstances<Item>.Get(id);
if (alreadyLoaded != null)
return alreadyLoaded;
return null;
}
}