2010-06-30 13 views
4

J'essaie d'implémenter une pile Undo/Redo dans une application C# sur laquelle je travaille en restaurant un objet dans un état précédent lorsque l'annulation est appelée. J'ai une classe « Action » qui ressemble fondamentalement à ceci:Implémentation de l'annulation/rétablissement à l'aide de MemberwiseClone

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; 
    } 
} 

Lorsqu'une action est lancée qui peut être refait, une nouvelle action est créée. Lorsque nous atteignons le nouvel état, finish() est appelé. Lorsque l'utilisateur veut refaire quelque chose, il appelle reverse() et restaure l'objet dans son état d'origine.

De toute évidence, cela ne fonctionne pas car les deux objets sont transmis par référence et l'objet se retrouve dans le nouvel état.

Ce que je veux vraiment faire est de pouvoir dire:

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

Malheureusement, cela ne fonctionne pas, et je reçois une erreur qui ressemble à:

Impossible d'accéder à la protection membre « object.MemberwiseClone() » par une qualification de type « foo.object »

Je veux créer une copie superficielle de l'état d'origine (copier tous les champs de valeur par val ue et tous les champs de référence par référence), mais je ne peux pas comprendre comment faire cela en utilisant des objets génériques, par opposition à l'implémentation IClonable dans chaque classe dont je pourrais éventuellement souhaiter restaurer l'état.

Quelqu'un peut-il offrir un aperçu?

+0

Je trouve cela un bon article sur la mise en œuvre des opérations Undo/Redo. Il donne plusieurs approches de mise en œuvre différentes: http://www.codeproject.com/KB/architecture/UndoRedoPart2.aspx – SwDevMan81

Répondre

2

Vous pouvez utiliser cette version de clonage d'un objet (Remarque: L'objet doit être sérialisable pour pouvoir utiliser cette fonction):

/// <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

Merci beaucoup - c'était très utile – Patrick

0

Pour ajouter un peu plus de perspicacité à cette question ... Je Nous avons remarqué que vous ne pouvez pas utiliser une référence d'instance pour appeler la méthode (parce que MemberwiseClone() est défini avec un identificateur de portée protégé). Au lieu de cela, vous devez utiliser le mot-clé this.

Pour l'exemple ci-dessus vous devez faire:

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