簡體   English   中英

獨立的普通紀念品

[英]Self-contained generic memento

親愛的程序員,

對於引用在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);
        }
    }
}

額外信息
我的猜測是,我在做/理解有關List的某些錯誤。

我還嘗試了Interlocked.Exchange,使用WeakReference來處理“ ref”,並將對象存儲到CareTaker對象中(並將該CareTaker存儲到List中),實現一些復制Property的事情...

而且...我只是看不到它。

我的預期結果將是PrestationInfo.Advance屬性,該屬性包含值2。

看來問題出在您對.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

嘗試這個:

更改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
}

有了ID后,您就可以通過以下方式獲取恢復的狀態:

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> ,並相應地調整代碼

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM