Автономное общее воспоминание
Дорогие коллеги-программисты,
Кажется, мне не хватает понимания того, как ссылки работают в C#.
Дело:
Я попытался реализовать своего рода прокси-сервер Memento, который бы обернул интерфейс и сохранил все параметры, которые мы предоставили вызовам метода, и сохранил их в списке.
При необходимости мы могли вызывать RestoreState, и объекты "сбрасывались" в исходное состояние.
Код:
Потребительский и модельный объект
class Program
{
static void Main(string[] args)
{
IMemento memento = new Memento();
PrestationInfo prestationInfo2 = new PrestationInfo { Advance = 2 };
memento.Add(prestationInfo2);
Console.WriteLine(prestationInfo2.Advance); //Expect 2
prestationInfo2.Advance = 1;
Console.WriteLine(prestationInfo2.Advance); //Expect 1
memento.RestoreState();
Console.WriteLine(prestationInfo2.Advance); //Expect 2, but still 1
Console.ReadKey();
}
}
[Serializable]
public class PrestationInfo
{
public int Advance { get; set; }
}
сувенир
public interface IMemento
{
void Add(object pItem);
void RestoreState();
}
public class Memento : IMemento
{
public Memento()
{
MementoList = new Dictionary<long, object>();
ReferenceList = new List<object>();
ObjectIDGenerator = new ObjectIDGenerator();
}
private ObjectIDGenerator ObjectIDGenerator { get; set; }
private Dictionary<long, object> MementoList { get; set; }
private List<object> ReferenceList { get; set; }
public void Add(object pItem)
{
bool firstTime;
long id = ObjectIDGenerator.GetId(pItem, out firstTime);
if (firstTime)
{
var mementoObject = DeepCopy(pItem);
MementoList.Add(id, mementoObject);
ReferenceList.Add(pItem);
}
}
public void RestoreState()
{
for (int i = 0; i < ReferenceList.Count; i++)
{
object reference = ReferenceList[i];
bool firstTime;
long id = ObjectIDGenerator.GetId(reference, out firstTime);
if (MementoList.ContainsKey(id))
{
object mementoObject = MementoList[id];
reference = mementoObject;
//reference = PropertyCopy<PrestationInfo>.CopyFrom(mementoObject as PrestationInfo); //Property copy
//Interlocked.Exchange(ref reference, mementoObject); //Also tried this
}
}
}
private static TCopy DeepCopy<TCopy>(TCopy pObjectToCopy)
{
using (MemoryStream memoryStream = new MemoryStream())
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(memoryStream, pObjectToCopy);
memoryStream.Position = 0;
return (TCopy)binaryFormatter.Deserialize(memoryStream);
}
}
}
Дополнительная информация
Я думаю, что я делаю / понимаю что-то не так в отношении списка.
Я также попробовал Interlocked.Exchange, поигравшись с "ref", используя WeakReference и сохранив объект в объекте CareTaker (и сохранив этот CareTaker в списке), реализовав некоторую функцию копирования свойства...
И... я просто не вижу этого.
Моим ожидаемым результатом было бы свойство PrestationInfo.Advance, содержащее значение 2. Но оно сохраняет
3 ответа
Попробуй это:
Изменить Add
метод:
public long Add(object pItem)
{
bool firstTime;
long id = ObjectIDGenerator.GetId(pItem, out firstTime);
if (firstTime)
{
var mementoObject = DeepCopy(pItem);
MementoList.Add(id, mementoObject);
ReferenceList.Add(pItem);
}
return id; // i need my memento! LOL
}
Вы также должны добавить этот метод доступа:
public object GetRestoredState(long id)
{
return MementoList[id]; // you should put some range check here
}
Теперь, когда у вас есть свой идентификатор, вы можете получить восстановленное состояние следующим образом:
memento.RestoreState();
prestationInfo2 = memento.GetRestoredState(savedId); // <-- you got this when you called the Add()...
Console.WriteLine(prestationInfo2.Advance); //Expect 2, but still 1
Последующие действия: вы также можете сделать IMemento
в IMemento<T>
и соответственно скорректируйте свой код
Похоже, проблема в вашем понимании ссылок в.NET
public void RestoreState()
{
for (int i = 0; i < ReferenceList.Count; i++)
{
object reference = ReferenceList[i];
bool firstTime;
long id = ObjectIDGenerator.GetId(reference, out firstTime);
if (MementoList.ContainsKey(id))
{
object mementoObject = MementoList[id];
reference = mementoObject;
//reference = PropertyCopy<PrestationInfo>.CopyFrom(mementoObject as PrestationInfo); //Property copy
//Interlocked.Exchange(ref reference, mementoObject); //Also tried this
}
}
}
Приведенный выше метод RestoreState ничего не возвращает, и вы строго работаете со ссылками, а не с их внутренним состоянием. Внутри вашего метода object reference
это местная ссылка. Это не то же самое, что внешний prestationInfo2
а твой метод просто делает reference
указать (сослаться) на ранее сохраненную копию состояния presentationInfo2
,
Вы можете изменить это примерно так:
public object RestoreState()
{
for (int i = 0; i < ReferenceList.Count; i++)
{
object reference = ReferenceList[i];
bool firstTime;
long id = ObjectIDGenerator.GetId(reference, out firstTime);
if (MementoList.ContainsKey(id))
{
object mementoObject = MementoList[id];
reference = mementoObject;
return reference;
}
}
return null;
}
А потом назовите это так:
presentationInfo2 = memento.RestoreState();
Если вы хотите, чтобы сувенир отслеживал объекты и волшебным образом восстанавливал их состояние, вам придется самим объектам знать о памятке, которая вводит связь или использует отражение для изменения внутреннего состояния отслеживаемых ссылок. По сути, вы не десериализуете сохраненное состояние в новый объект, а используете отражение, чтобы перезаписать ранее сохраненное внутреннее состояние в ссылку на отслеживаемый объект.
Будьте осторожны при хранении ссылок, используя WeakReference, иначе вы обнаружите хороший случай утечки памяти.
Memento.Add нужен модификатор ref для доступа к исходному указателю ссылочного типа.
https://msdn.microsoft.com/en-us/library/14akc2c7.aspx?f=255&MSPPError=-2147217396