Автономное общее воспоминание

Дорогие коллеги-программисты,

Кажется, мне не хватает понимания того, как ссылки работают в 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

Другие вопросы по тегам