1

亲爱的程序员,

我似乎对 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”,使用 Wea​​kReference 并将对象存储到 CareTaker 对象中(并将 CareTaker 存储到列表中),实现一些复制属性的东西......

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

我的预期结果将是 PrestationInfo.Advance 包含值 2 的属性。但它保持

4

3 回答 3

0

看起来问题在于您对 .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();

如果您希望备忘录跟踪对象并神奇地恢复它们的状态,您必须让对象本身了解引入耦合的备忘录或使用反射来修改跟踪的引用内部状态。基本上,您不会将持久状态反序列化为新对象,而是使用反射将先前存储的内部状态覆盖到被跟踪对象引用中。

不过要小心使用 Wea​​kReference 存储引用,否则你会发现自己遇到了一个很好的内存泄漏情况。

于 2015-09-28T18:54:56.190 回答
0

Memento.Add 需要一个 ref 参数修饰符来访问原始引用类型指针。

https://msdn.microsoft.com/en-us/library/14akc2c7.aspx?f=255&MSPPError=-2147217396

于 2015-09-28T18:57:51.377 回答
0

尝试这个:

改变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成 a IMemento<T>,并相应地调整您的代码

于 2015-09-28T19:00:08.393 回答