2013-06-24 35 views
0

在這個問題中 Transactions for C# objects? 用戶nicolas2008發佈了能夠回滾到更改對象的代碼。我粘貼下面的代碼。 我想問的是,該代碼可以安全使用,或者你看到一些危險嗎?還有它與Memento模式相比如何?單線程對象在C#中回滾

public sealed class ObjectTransaction : IDisposable 
{ 
    bool m_isDisposed; 

    Dictionary<object, object> sourceObjRefHolder; 
    object m_backup; 
    object m_original; 

    public ObjectTransaction(object obj) 
    { 
     sourceObjRefHolder = new Dictionary<object, object>(); 
     m_backup = processRecursive(obj, sourceObjRefHolder, new CreateNewInstanceResolver()); 
     m_original = obj; 
    } 

    public void Dispose() 
    { 
     Rollback(); 
    } 

    public void Rollback() 
    { 
     if (m_isDisposed) 
      return; 

     var processRefHolder = new Dictionary<object, object>(); 
     var targetObjRefHolder = sourceObjRefHolder.ToDictionary(x => x.Value, x => x.Key); 
     var originalRefResolver = new DictionaryRefResolver(targetObjRefHolder); 
     processRecursive(m_backup, processRefHolder, originalRefResolver); 

     dispose(); 
    } 

    public void Commit() 
    { 
     if (m_isDisposed) 
      return; 

     //do nothing 
     dispose(); 
    } 

    void dispose() 
    { 
     sourceObjRefHolder = null; 
     m_backup = null; 
     m_original = null; 
     m_isDisposed = true; 
    } 

    object processRecursive(object objSource, Dictionary<object, object> processRefHolder, ITargetObjectResolver targetResolver) 
    { 
     if (objSource == null) return null; 
     if (objSource.GetType() == typeof(string) || objSource.GetType().IsClass == false) return objSource; 
     if (processRefHolder.ContainsKey(objSource)) return processRefHolder[objSource]; 

     Type type = objSource.GetType(); 
     object objTarget = targetResolver.Resolve(objSource); 
     processRefHolder.Add(objSource, objTarget); 

     if (type.IsArray) 
     { 
      Array objSourceArray = (Array)objSource; 
      Array objTargetArray = (Array)objTarget; 
      for (int i = 0; i < objSourceArray.Length; ++i) 
      { 
       object arrayItemTarget = processRecursive(objSourceArray.GetValue(i), processRefHolder, targetResolver); 
       objTargetArray.SetValue(arrayItemTarget, i); 
      } 
     } 
     else 
     { 
      IEnumerable<FieldInfo> fieldsInfo = FieldInfoEnumerable.Create(type); 

      foreach (FieldInfo f in fieldsInfo) 
      { 
       if (f.FieldType == typeof(ObjectTransaction)) continue; 

       object objSourceField = f.GetValue(objSource); 
       object objTargetField = processRecursive(objSourceField, processRefHolder, targetResolver); 

       f.SetValue(objTarget, objTargetField); 
      } 
     } 

     return objTarget; 
    } 

    interface ITargetObjectResolver 
    { 
     object Resolve(object objSource); 
    } 

    class CreateNewInstanceResolver : ITargetObjectResolver 
    { 
     public object Resolve(object sourceObj) 
     { 
      object newObject = null; 
      if (sourceObj.GetType().IsArray) 
      { 
       var length = ((Array)sourceObj).Length; 
       newObject = Activator.CreateInstance(sourceObj.GetType(), length); 
      } 
      else 
      { 
       //no constructor calling, so no side effects during instantiation 
       newObject = System.Runtime.Serialization.FormatterServices.GetUninitializedObject(sourceObj.GetType()); 

       //newObject = Activator.CreateInstance(sourceObj.GetType()); 
      } 
      return newObject; 
     } 
    } 

    class DictionaryRefResolver : ITargetObjectResolver 
    { 
     readonly Dictionary<object, object> m_refHolder; 

     public DictionaryRefResolver(Dictionary<object, object> refHolder) 
     { 
      m_refHolder = refHolder; 
     } 

     public object Resolve(object sourceObj) 
     { 
      if (!m_refHolder.ContainsKey(sourceObj)) 
       throw new Exception("Unknown object reference"); 

      return m_refHolder[sourceObj]; 
     } 
    } 
} 

class FieldInfoEnumerable 
{ 
    public static IEnumerable<FieldInfo> Create(Type type) 
    { 
     while (type != null) 
     { 
      var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); 

      foreach (FieldInfo fi in fields) 
      { 
       yield return fi; 
      } 

      type = type.BaseType; 
     } 
    } 
} 
+0

這個類是否被多個線程使用? – alu

+0

nope,如在主題中,我認爲只有單線程案例 – user1121956

回答

1

我的代碼創建支持回滾對象真的深拷貝
它不會引發任何事件。
它不調用任何方法,構造函數或屬性訪問器。

它可以是危險的,如果對象樹包含引用:

  • 非託管資源(它們不能被複制正確,我認爲),
  • 共享對象(因爲他們也將被複制/軋製背部)。

您必須分析此風險。 我建議你添加類型過濾器(在processRecursive方法的開始處)或者通過其他字段元數據過濾(在foreach循環中)來跳過執行深度複製這樣的對象。例如:

object processRecursive(object objSource, Dictionary<object, object> processRefHolder, ITargetObjectResolver targetResolver) 
    { 
     if (objSource == null) return null; 
     //perform filter by type: 
     if (typeof(System.Data.IDbConnection).IsAssignableFrom(objSource)) return objSource;    
     //... 

      foreach (FieldInfo f in fieldsInfo) 
      { 
       if (f.FieldType == typeof(ObjectTransaction)) continue; 

       object objSourceField = f.GetValue(objSource); 
       object objTargetField = Attribute.IsDefined(f,typeof(MyAttributeForSkipTransaction)) //perform filter by field attribute 
            ? objSourceField 
            : processRecursive(objSourceField, processRefHolder, targetResolver); 

       f.SetValue(objTarget, objTargetField); 
      } 

     //... 
    } 

在備忘錄模式中ObjectTransaction扮演Memento對象的角色:存儲對象的狀態。
SaveToMemento:創建新的ObjectTransaction(thisObj)。
RestoreFromMemento:rollback創建了ObjectTransaction。

+0

謝謝你的回答和提示:) – user1121956