C#4 中的 Memento 模式是如何实现的?

发布于 2024-12-28 18:39:51 字数 132 浏览 0 评论 0原文

备忘录模式本身看起来非常简单。我正在考虑实现与维基百科示例相同的功能,但在此之前,C# 是否有任何语言功能可以使其更易于实现或使用?

The Memento Pattern itself seems pretty straight forward. I'm considering implementing the same as the wikipedia example, but before I do are there any language features of C# that make it easier to implement or use?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

音盲 2025-01-04 18:39:51

一个明显的功能是泛型,实现通用纪念品将允许您将其用于任何您想要的对象。

您将看到的许多示例将使用字符串(包括当前对此问题的答复中的所有字符串)作为状态,这是一个问题,因为它是 .NET 中少数不可变的类型之一。

在处理可变对象(如任何具有 setter 属性的引用类型)时,您必须记住,当您保存备忘录时,您需要创建该对象的深层副本。否则,每当您更改原始对象时,您都会更改您的纪念品。

您可以使用 protobuf-netjson.net 因为它们不需要像普通的 .net 序列化机制那样使用可序列化属性标记对象。

Codeproject 关于通用备忘录实现的文章很少,但它们倾向于跳过深层复制部分:

C# 中撤消-重做的通用备忘录模式

纪念品设计模式

One obvious feature would be generics, implementing an generic memento will allow you to use it for any object you want.

Many examples that you will see will use a string (including all those currently among the replies to this question) as state which is a problem since it's one of the few types in .NET which are immutable.

When dealing with mutable objects (like any reference type with a setter-property) you have to remember though that when you save the memento you need to create a deepcopy of the object. Otherwise whenever you change your original object you will change your memento.

You could do this by using a serializer like protobuf-net or json.net since they don't require you to mark your objects with serializable attribute like the normal .net serialization mechanism does.

Codeproject have few articles about generic memento implementations, but they tend to skip the deepcopy part:

Generic Memento Pattern for Undo-Redo in C#

Memento Design Pattern

嗫嚅 2025-01-04 18:39:51

我不知道有任何内置的方式来支持 Memento 模式。
我看到了一些使用 .NET Mock 框架 的实现,其中实际上,会创建对象的克隆,并且可以使用数据作为字段,但我认为这是一种开销。

通常,您可能也会在撤消/重做时使用 Memento 模式。在这种情况下,撤消/重做堆栈上的数据最好尽可能少,因此自定义可撤消对象所追求的。

希望这有帮助。

I'm not aware of any already built-in way to support Memento pattern.
I see a couple of implementations by using .NET Mock frameworks, where in practise a clone of the object is created and can be field with a data, but I consider it kind of overhead.

The use Memento patter on Undo/Redo usually, probably you too. In this case, it's better to have as less data on Undo/Redo stack as possible, so the custom undoable object is something that I would go for.

Hope this helps.

ゝ杯具 2025-01-04 18:39:51

有一点可以让这种模式在 C# 中的编写速度稍快一些,那就是任何状态字段都可以声明为 public readonly,因此您不需要属性或“get”方法来访问它们。

这是包含 public readonly 的直接转换。

class Originator 
{
    private string state;
    // The class could also contain additional data that is not part of the
    // state saved in the memento.

    public void Set(string state) 
    {
        Console.WriteLine("Originator: Setting state to " + state);
        this.state = state;
    }

    public Memento SaveToMemento() 
    {
        Console.WriteLine("Originator: Saving to Memento.");
        return new Memento(state);
    }

    public void RestoreFromMemento(Memento memento) 
    {
        state = memento.SavedState;
        Console.WriteLine("Originator: State after restoring from Memento: " + state);
    }

    public class Memento 
    {
        public readonly string SavedState;

        public Memento(string stateToSave)  
        {
            SavedState = stateToSave;
        }
    }
}

class Caretaker 
{
    static void Main(string[] args) 
    {
        List<Originator.Memento> savedStates = new List<Originator.Memento>();

        Originator originator = new Originator();
        originator.Set("State1");
        originator.Set("State2");
        savedStates.Add(originator.SaveToMemento());
        originator.Set("State3");
        // We can request multiple mementos, and choose which one to roll back to.
        savedStates.Add(originator.SaveToMemento());
        originator.Set("State4");

        originator.RestoreFromMemento(savedStates[1]);   
    }
}

There is one thing that will make this pattern marginally quicker to write in C# and that is that any state fields can be declared as public readonly so you don't need properties or 'get' methods to access them.

Here is a straight conversion with public readonly included.

class Originator 
{
    private string state;
    // The class could also contain additional data that is not part of the
    // state saved in the memento.

    public void Set(string state) 
    {
        Console.WriteLine("Originator: Setting state to " + state);
        this.state = state;
    }

    public Memento SaveToMemento() 
    {
        Console.WriteLine("Originator: Saving to Memento.");
        return new Memento(state);
    }

    public void RestoreFromMemento(Memento memento) 
    {
        state = memento.SavedState;
        Console.WriteLine("Originator: State after restoring from Memento: " + state);
    }

    public class Memento 
    {
        public readonly string SavedState;

        public Memento(string stateToSave)  
        {
            SavedState = stateToSave;
        }
    }
}

class Caretaker 
{
    static void Main(string[] args) 
    {
        List<Originator.Memento> savedStates = new List<Originator.Memento>();

        Originator originator = new Originator();
        originator.Set("State1");
        originator.Set("State2");
        savedStates.Add(originator.SaveToMemento());
        originator.Set("State3");
        // We can request multiple mementos, and choose which one to roll back to.
        savedStates.Add(originator.SaveToMemento());
        originator.Set("State4");

        originator.RestoreFromMemento(savedStates[1]);   
    }
}
阳光下的泡沫是彩色的 2025-01-04 18:39:51

我发现一个使用泛型 here

#region Originator
public class Originator<T>
{
   #region Properties
   public T State { get; set; }
   #endregion
   #region Methods
   /// <summary>
   /// Creates a new memento to hold the current
   /// state
   /// </summary>
   /// <returns>The created memento</returns>
   public Memento<T> SaveMemento()
   {
      return (new Memento<T>(State));
   }
   /// <summary>
   /// Restores the state which is saved in the given memento
   /// </summary>
   /// <param name="memento">The given memento</param>
   public void RestoreMemento(Memento<T> memento)
   {
      State = memento.State;
   }
   #endregion
}
#endregion
#region Memento
public class Memento<T>
{
   #region Properties
   public T State { get; private set; }
   #endregion
   #region Ctor
   /// <summary>
   /// Construct a new memento object with the
   /// given state
   /// </summary>
   /// <param name="state">The given state</param>
   public Memento(T state)
   {
      State = state;
   }
   #endregion
}
#endregion
#region Caretaker
public class Caretaker<T>
{
   #region Properties
   public Memento<T> Memento { get; set; }
   #endregion
}
#endregion
#region Originator
public class Originator<T>
{
   #region Properties
   public T State { get; set; }
   #endregion
   #region Methods
   /// <summary>
   /// Creates a new memento to hold the current
   /// state
   /// </summary>
   /// <returns>The created memento</returns>
   public Memento<T> SaveMemento()
   {
      return (new Memento<T>(State));
   }
   /// <summary>
   /// Restores the state which is saved in the given memento
   /// </summary>
   /// <param name="memento">The given memento</param>
   public void RestoreMemento(Memento<T> memento)
   {
      State = memento.State;
   }
   #endregion
}
#endregion
#region Memento
public class Memento<T>
{
   #region Properties
   public T State { get; private set; }
   #endregion
   #region Ctor
   /// <summary>
   /// Construct a new memento object with the
   /// given state
   /// </summary>
   /// <param name="state">The given state</param>
   public Memento(T state)
   {
      State = state;
   }
   #endregion
}
#endregion
#region Caretaker
public class Caretaker<T>
{
   #region Properties
   public Memento<T> Memento { get; set; }
   #endregion
}
#endregion

像这样使用:

   Originator<string> org = new Originator<string>();
   org.State = "Old State";
   // Store internal state in the caretaker object
   Caretaker<string> caretaker = new Caretaker<string>();
   caretaker.Memento = org.SaveMemento();
   Console.WriteLine("This is the old state: {0}", org.State);
   org.State = "New state";
   Console.WriteLine("This is the new state: {0}", org.State);
   // Restore saved state from the caretaker
   org.RestoreMemento(caretaker.Memento);
   Console.WriteLine("Old state was restored: {0}", org.State);
   // Wait for user
   Console.Read();

正如 @Simon Skov Boisen 提到的,这仅适用于不可变数据并且需要 深层复制

I've found one using Generics here:

#region Originator
public class Originator<T>
{
   #region Properties
   public T State { get; set; }
   #endregion
   #region Methods
   /// <summary>
   /// Creates a new memento to hold the current
   /// state
   /// </summary>
   /// <returns>The created memento</returns>
   public Memento<T> SaveMemento()
   {
      return (new Memento<T>(State));
   }
   /// <summary>
   /// Restores the state which is saved in the given memento
   /// </summary>
   /// <param name="memento">The given memento</param>
   public void RestoreMemento(Memento<T> memento)
   {
      State = memento.State;
   }
   #endregion
}
#endregion
#region Memento
public class Memento<T>
{
   #region Properties
   public T State { get; private set; }
   #endregion
   #region Ctor
   /// <summary>
   /// Construct a new memento object with the
   /// given state
   /// </summary>
   /// <param name="state">The given state</param>
   public Memento(T state)
   {
      State = state;
   }
   #endregion
}
#endregion
#region Caretaker
public class Caretaker<T>
{
   #region Properties
   public Memento<T> Memento { get; set; }
   #endregion
}
#endregion
#region Originator
public class Originator<T>
{
   #region Properties
   public T State { get; set; }
   #endregion
   #region Methods
   /// <summary>
   /// Creates a new memento to hold the current
   /// state
   /// </summary>
   /// <returns>The created memento</returns>
   public Memento<T> SaveMemento()
   {
      return (new Memento<T>(State));
   }
   /// <summary>
   /// Restores the state which is saved in the given memento
   /// </summary>
   /// <param name="memento">The given memento</param>
   public void RestoreMemento(Memento<T> memento)
   {
      State = memento.State;
   }
   #endregion
}
#endregion
#region Memento
public class Memento<T>
{
   #region Properties
   public T State { get; private set; }
   #endregion
   #region Ctor
   /// <summary>
   /// Construct a new memento object with the
   /// given state
   /// </summary>
   /// <param name="state">The given state</param>
   public Memento(T state)
   {
      State = state;
   }
   #endregion
}
#endregion
#region Caretaker
public class Caretaker<T>
{
   #region Properties
   public Memento<T> Memento { get; set; }
   #endregion
}
#endregion

Used like this:

   Originator<string> org = new Originator<string>();
   org.State = "Old State";
   // Store internal state in the caretaker object
   Caretaker<string> caretaker = new Caretaker<string>();
   caretaker.Memento = org.SaveMemento();
   Console.WriteLine("This is the old state: {0}", org.State);
   org.State = "New state";
   Console.WriteLine("This is the new state: {0}", org.State);
   // Restore saved state from the caretaker
   org.RestoreMemento(caretaker.Memento);
   Console.WriteLine("Old state was restored: {0}", org.State);
   // Wait for user
   Console.Read();

As @Simon Skov Boisen mentions this will only work for immutable data and requires a deep copy.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文