C# 如何获取集合的不可变值
作为表示体系结构的一部分,我有一个实现IEditableObject的基类,因此当BeginEdit()获取状态快照时,将通过反射为所有可写、非集合属性创建字典C# 如何获取集合的不可变值,c#,algorithm,design-patterns,collections,C#,Algorithm,Design Patterns,Collections,作为表示体系结构的一部分,我有一个实现IEditableObject的基类,因此当BeginEdit()获取状态快照时,将通过反射为所有可写、非集合属性创建字典 var propertyInfos = GetType().GetWritableNonCollectionPropertyInfos(); var dic = propertyInfos .ToDictionary(pi => pi.Name, pi => pi.GetValue(this, nul
var propertyInfos = GetType().GetWritableNonCollectionPropertyInfos();
var dic = propertyInfos
.ToDictionary(pi => pi.Name, pi => pi.GetValue(this, null));
此词典是IsDirty跟踪和回滚CancelEdit()的基础
默认行为排除集合的原因是,它们通常是不必要的,枚举起来可能很昂贵,而且通常是一种痛苦
关于这个问题,我有一个用例,其中集合状态是必需的。它是一组自定义对象,一次很少有超过5个的对象。从上面的代码中可以看到,集合的值最终将成为集合本身的引用;它会随着集合的变化而变化,因此您无法判断它是否真的很脏
因此,我需要为集合存储一些值,这些值将是不可变的,并且在集合更改时与集合的值相比较。以下是我的大致想法:
if(IncludeCollections) {
var collectionOnlyDictionary = GetCollectionOnlyDictionary();
foreach (var kvp in collectionOnlyDictionary) {
dic.Add(GetCollectionKey(kvp.Key), kvp.Value);
}
}
protected override object GetLatestCollectionValue(string key) {
if (key != _key_AgeHistoryCollection)
return null;
return GetImmutableCollectionValue(AgeHistory);
}
protected static object GetImmutableCollectionValue<T>(ICollection<T> c) {
var hash = c.Count.GetHashCode();
unchecked {
hash = c.Aggregate(hash, (current, i) => current += i.GetHashCode());
}
return hash;
}
受保护的覆盖对象GetLatestCollectionValue(字符串键){
if(key!=\u key\u AgeHistoryCollection)
返回null;
返回GetImmutableCollectionValue(AgeHistory);
}
受保护的静态对象GetImmutableCollectionValue(ICollection c){
var hash=c.Count.GetHashCode();
未经检查{
hash=c.Aggregate(hash,(current,i)=>current+=i.GetHashCode());
}
返回散列
}
public static int GetVmCollectionHashCode<T>(this ICollection<T> c) where T : ViewModelBase
{
if (c == null)
return 0;
var hash = c.Count.GetHashCode();
unchecked
{
hash = c.
Aggregate(hash, (current, i) => current += i.DisplayName.GetHashCode());
}
return hash;
}
public static int GetVmCollectionHashCode(此ICollection c),其中T:ViewModelBase
{
如果(c==null)
返回0;
var hash=c.Count.GetHashCode();
未经检查
{
hash=c。
聚合(hash,(current,i)=>current+=i.DisplayName.GetHashCode());
}
返回散列;
}
但我真正领悟到的是,与其让EditingNotifier超类在集合处理方面变得混乱和复杂,不如将集合本身封装到一个类中,并在修改后通知它所包含的类,因此:
/// <summary>
/// A collection with the ability to broadcast <see cref="Messenger"/> notifications
/// when the collection is altered. Subscribers that need to know if they are dirty
/// because this collection was modified can use the information to calculate a stateful
/// proerty using <see cref="EditingHelpers.GetVmCollectionHashCode{T}"/>,
/// </summary>
public class PcmDetailCollectionVm : ObservableCollection<PcmDetailVm>
{
#region Creation
public PcmDetailCollectionVm(IEnumerable<PcmDetailVm> pcms) : base(pcms) {
// set INPC for each vm
foreach (var vm in this)
vm.PropertyChanged += OnDetailVmChanged;
}
#endregion
#region Collection Modification Handlers
public void AddDetailVm(PcmDetailVm item) {
Add(item);
item.PropertyChanged += OnDetailVmChanged;
Messenger.GetInstance.Notify(MessengerMessages.PcmCollectionChanged, this);
}
public void RemoveDetailVm(PcmDetailVm item) {
Remove(item);
Messenger.GetInstance.Notify(MessengerMessages.PcmCollectionChanged, this);
}
private readonly string _propName_DisplayName = ExprHelper.GetPropertyName<ViewModelBase>(vm => vm.DisplayName);
private void OnDetailVmChanged(object sender, PropertyChangedEventArgs e) {
if (e.PropertyName == _propName_DisplayName)
Messenger.GetInstance.Notify(MessengerMessages.PcmCollectionChanged, this);
}
#endregion
}
//
///具有广播通知功能的集合
///当集合被更改时。需要知道自己是否肮脏的订户
///由于此集合已修改,因此可以使用该信息计算有状态
///proerty使用,
///
公共类PcmDetailCollectionVm:ObservableCollection
{
#区域创建
公共PcmDetailCollectionVm(IEnumerable pcms):基本(pcms){
//为每个vm设置INPC
foreach(在本例中为var-vm)
vm.PropertyChanged+=OnDetailVmChanged;
}
#端区
#区域集合修改处理程序
公共无效AddDetailVm(PcmDetailVm项){
增加(项目);
item.PropertyChanged+=OnDetailVmChanged;
Messenger.GetInstance.Notify(Messenger Messages.PcmCollectionChanged,this);
}
已移除的公共无效etailvm(PcmDetailVm项){
删除(项目);
Messenger.GetInstance.Notify(Messenger Messages.PcmCollectionChanged,this);
}
私有只读字符串\u propName\u DisplayName=ExprHelper.GetPropertyName(vm=>vm.DisplayName);
私有void OnDetailVmChanged(对象发送方,PropertyChangedEventArgs e){
如果(例如PropertyName==\u propName\u DisplayName)
Messenger.GetInstance.Notify(Messenger Messages.PcmCollectionChanged,this);
}
#端区
}
现在,包含类只需要一个简单的属性,该属性在收到通知时更新,并且状态跟踪正常进行
///包含类
Messenger.GetInstance.Register(MessengerMessages.PcmCollectionChanged, (Action<PcmDetailCollectionVm>)(OnPcmCollectionChanged));
public int DetailVmsHashCode
{
get { return _detailVmsHashCode; }
protected set {
if (_detailVmsHashCode == value)
return;
_detailVmsHashCode = value;
Notify(() => DetailVmsHashCode);
}
}
private int _detailVmsHashCode;
private void OnPcmCollectionChanged(PcmDetailCollectionVm obj)
{
if(!ReferenceEquals(obj, DetailVms))
return;
DetailVmsHashCode = DetailVms.GetVmCollectionHashCode();
}
Messenger.GetInstance.Register(MessengerMessages.PcmCollectionChanged,(Action)(OnPcmCollectionChanged));
公共int-DetailVmsHashCode
{
获取{return}
保护集{
if(_detailVmsHashCode==值)
返回;
_detailVmsHashCode=值;
通知(()=>DetailVmsHashCode);
}
}
私有整数码;
PCMCollectionChanged上的私有无效(PcmDetailCollectionVm obj)
{
如果(!ReferenceEquals(obj,DetailVms))
返回;
DetailVmsHashCode=DetailVms.GetVmCollectionHashCode();
}
生活又好起来了…这里有些好东西。。。我不知道:tl;但是最重要的是你需要遵循问答的形式:把问题作为问题来提出。然后,在回答问题的单独帖子中,发布您的解决方案。准备好接受一些批评。。。你可能会发现其他人发布了一些你应该接受的东西,而不是你想要的答案。这也有助于你的tl;“把这篇文章分成几部分来解决这个问题。”@JoelCoehoorn。硬币的另一面是,上下文和解决方案动机可能没有被理解。我确实以一句话结束了最初的帖子