使用MemberwiseClone实现撤消/重做

问题描述 投票:0回答:3

我正在尝试在 C# 应用程序中实现撤消/重做堆栈,方法是在调用撤消时将对象恢复到之前的状态。我有一个“Action”类,基本上看起来像这样:

class Action
{
    object old_state;
    object new_state;

    public Action(object old)
    {
        old_state = old;
    }

    public void finish(object new_obj)
    {
        new_state = new_obj;
    }

    public void reverse()
    {
        new_state = old_state;
    }
}

当启动可重做的操作时,将创建一个新操作。当我们到达新状态时,finish() 被调用。当用户想要重做某事时,它会调用reverse()并将对象恢复到原始状态。

显然这不起作用,因为两个对象都是通过引用传递的,并且对象最终处于新状态。

我真正想做的是能够说:

public Action(object old)
{
    old_state = old.MemberwiseClone();
}

不幸的是,这不起作用,我收到一个如下所示的错误:

无法通过“foo.object”类型的限定符访问受保护成员“object.MemberwiseClone()”

我想创建原始状态的浅表副本(按值复制所有值字段,并按引用复制所有引用字段),但我不知道如何使用通用对象来做到这一点,而不是实现

IClonable
在我可能希望恢复状态的每一堂课中。

任何人都可以提供任何见解吗?

c# clone
3个回答
2
投票

您可以使用此版本的克隆对象(注意:该对象必须可序列化才能使用此功能):

  /// <summary>
  /// Clones Any Object.
  /// </summary>
  /// <param name="objectToClone">The object to clone.</param>
  /// <return>The Clone</returns>
  public static T Clone<T>(T objectToClone)
  {
     T cloned_obj = default(T);
     if ((!Object.ReferenceEquals(objectToClone, null)) && (typeof(T).IsSerializable))
     {
        System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bin_formatter = null;
        Byte[] obj_bytes = null;

        using (MemoryStream memory_stream = new MemoryStream(1000))
        {
           bin_formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
           try
           {
              bin_formatter.Serialize(memory_stream, objectToClone);
           }
           catch (Exception) { }
           obj_bytes = memory_stream.ToArray();
        }

        using (MemoryStream memory_stream = new MemoryStream(obj_bytes))
        {
           try
           {
              cloned_obj = (T)bin_formatter.Deserialize(memory_stream);
           }
           catch (Exception) { }
        }
     }
     return cloned_obj;
  }

0
投票

为了对这个问题有更多的了解...我注意到您不能使用实例引用来调用该方法(因为 MemberwiseClone() 是使用受保护的范围标识符定义的)。相反,您必须使用

this
关键字。

对于上面给出的示例,您必须执行以下操作:

public Action(object old)
{
    old_state = this.MemberwiseClone();
}

0
投票

AS BinaryFormatter 已过时:https://learn.microsoft.com/en-us/dotnet/core/compatibility/serialization/5.0/binaryformatter-serialization-obsolete。我已经使用 JsonSerializer 重新设计了 Peter 的解决方案,并将其发布在这里以防有帮助

using System.IO;
using System.Text.Json;

namespace Reception.WinApp.Components;

public interface ICloner
{
    /// <summary>
    /// Clones Any Serializable Object. Object with [Serializable] attribute set on it and any objects it contains.
    /// </summary>
    /// <param name="objectToClone">The object to clone.</param>
    /// <returns>The Clone</returns>
    /// <remarks>https://stackoverflow.com/questions/3153353/implementing-undo-redo-using-memberwiseclone</remarks>
    T Clone<T>(T objectToClone);
}

public class Cloner : ICloner
{
    public T Clone<T>(T objectToClone)
    {
        var clonedObject = default(T);
        if (objectToClone is not null && (typeof(T).IsSerializable))
        {
            byte[] objBytes;

            using (var memoryStream = new MemoryStream(1000))
            {
                JsonSerializer.Serialize(memoryStream, objectToClone);
                objBytes = memoryStream.ToArray();
            }

            using (var memoryStream = new MemoryStream(objBytes))
            {
                clonedObject = JsonSerializer.Deserialize<T>(memoryStream);
            }
        }

        return clonedObject;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.