我一直在尝试检测C#中普通对象的变化。目标是为一堆数据对象提供容器类型类,这些数据对象可以在其中任何一个更改时作出反应。为了好玩,我想看看是否可以在容器类中完成所有工作,而不是诉诸于对象本身的属性和脏标志或事件。
我感到好奇的是,是否有一种智能,快速和有效的方法。我的尝试在下面,并且没有一个(为了一个开始,每个帧都需要调用“CheckStates'方法!”)我将其限制为仅允许每个实例一个实例类型,适合我的需要。
请注意,传入的对象可能如下所示:
[Serializable]
public class PlayerInfo
{
public string name = string.Empty;
public int score = 0;
}
然后是容器:
public class AppState
{
private class StateData
{
public System.Object instance = null;
public Byte[] currentState = new Byte[0];
public Byte[] previousState = new Byte[0];
}
private Dictionary<Type, StateData> _allStates = new Dictionary<Type, StateData>();
private BinaryFormatter _formatter = new BinaryFormatter();
private MemoryStream _memoryStream = new MemoryStream();
public T GetState<T>() where T : class, new()
{
T state = default(T);
var stateType = typeof(T);
StateData stateData;
if(_allStates.TryGetValue(stateType, out stateData))
{
state = ReadData<T>(stateData);
}
else
{
var newState = CreateData<T>(out state);
_allStates[stateType] = newState;
}
return state;
}
public void CheckStates()
{
foreach(var state in _allStates)
{
if(HasChanged(state.Value))
{
Console.WriteLine(state.Key.ToString() + " has changed");
UpdateState(state.Value);
}
}
}
private StateData CreateData<T>(out T instance) where T : class, new()
{
instance = new T();
var stateData = new StateData();
stateData.instance = instance;
_formatter.Serialize(_memoryStream, instance);
var bytes = _memoryStream.ToArray();
stateData.currentState = bytes;
stateData.previousState = bytes;
return stateData;
}
private T ReadData<T>(StateData data) where T : class, new()
{
return data.currentState as T;
}
private bool HasChanged(StateData data)
{
_memoryStream.Position = 0;
_formatter.Serialize(_memoryStream, data.instance);
var current = _memoryStream.ToArray();
var previous = data.previousState;
if(current.Length != previous.Length)
{
return true;
}
for(int i = 0; i < current.Length; ++i)
{
if(current[i] != previous[i])
{
return true;
}
}
return false;
}
private void UpdateState(StateData data)
{
_memoryStream.Position = 0;
_formatter.Serialize(_memoryStream, data.instance);
data.previousState = _memoryStream.ToArray();
}
}
我能想到的替代方案是:
编辑:应该补充一点,它不需要是线程安全的
答案 0 :(得分:0)
我不会将可序列化的类视为POCO,因为您正在设计类以便它们与您的更改检测机制一起使用。所以我不会简单地称它们为。
您的替代方案:
使用结构而不是可序列化的类
不要使用可变结构Why are mutable structs “evil”?。如果你的结构是不可变的,那么你也可以通过引用传递,即有一个类。
得到&#39; get&#39;方法返回IDisposable包装器
我不确定你指的是什么方法。
<强>代理强>
一种替代方法是允许后代代理对对setter的调用作出反应:
public class PlayerInfo
{
public virtual string Name { get; set; }
public virtual int Score { get; set; }
}
public class PlayerInfoDetection : PlayerInfo
{
public int Revision { get; private set; }
public override string Name
{
set
{
base.Name = value;
Revision++;
}
}
public override int Score
{
set
{
base.Score = value;
Revision++;
}
}
}
private static void Example()
{
PlayerInfo pi = new PlayerInfoDetection();
Console.WriteLine(((PlayerInfoDetection)pi).Revision);
pi.Name = "weston";
Console.WriteLine(((PlayerInfoDetection)pi).Revision);
pi.Score = 123;
Console.WriteLine(((PlayerInfoDetection)pi).Revision);
}
这就是NHibernate&#34;观看&#34;从数据库中获取的对象,以及为什么每个对象属性必须在NHibernate中是虚拟的。
面向方面
使用像post sharp这样的产品可以实现同样的目的,您可以在必须更改修订时对类进行注释以告知它。
public class PlayerInfo
{
public int Revision { get; private set; }
public string Name { get; [IncreaseRevision] set; }
public int Score { get; [IncreaseRevision] set; }
}
利用良好实施的哈希函数
当对象位于容器(如哈希集)中时,哈希函数不应更改其值。我们可以利用它来检测变化。
缺点请注意,任何哈希冲突都会产生不正确的结果。这包括重复。
[TestClass]
public class ChangeDetectUnitTest
{
public class ChangeDetectList<T>
{
private readonly List<T> list = new List<T>();
private readonly ISet<T> hashes = new HashSet<T>();
public bool HasChanged(T t)
{
return !hashes.Contains(t);
}
public void Add(T t)
{
list.Add(t);
hashes.Add(t);
}
public void Reset()
{
hashes.Clear();
foreach (var t in list)
hashes.Add(t);
}
}
public class PlayerInfo
{
public string Name { get; set; }
public int Score { get; set; }
public override int GetHashCode()
{
//every field that you want to detect must feature in the hashcode
return (Name ?? "").GetHashCode() * 31 + Score;
}
public override bool Equals(object obj)
{
return Equals(obj as PlayerInfo);
}
public bool Equals(PlayerInfo other)
{
if (other == null) return false;
return Equals(other.Name, Name) && Score == Score;
}
}
private ChangeDetectList<PlayerInfo> list;
[TestInitialize]
public void Setup()
{
list = new ChangeDetectList<PlayerInfo>();
}
[TestMethod]
public void Can_add()
{
var p1 = new PlayerInfo();
list.Add(p1);
Assert.IsFalse(list.HasChanged(p1));
}
[TestMethod]
public void Can_detect_change()
{
var p1 = new PlayerInfo();
list.Add(p1);
p1.Name = "weston";
Assert.IsTrue(list.HasChanged(p1));
}
[TestMethod]
public void Can_reset_change()
{
var p1 = new PlayerInfo();
list.Add(p1);
p1.Name = "weston";
list.Reset();
Assert.IsFalse(list.HasChanged(p1));
}
}