在这个问题上 Transactions for C# objects? 用户nicolas2008发布了能够回滚到更改对象的代码。我粘贴下面的代码。 我想问一下,代码是否可以安全使用,或者您是否看到了一些危险?它与Memento模式相比如何?
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 :(得分: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);
}
//...
}
在memento模式中,ObjectTransaction扮演Memento对象的角色:存储对象的状态 SaveToMemento:创建新的ObjectTransaction(thisObj) RestoreFromMemento:rollback created ObjectTransaction。