C# 如何获取集合的不可变值

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

作为表示体系结构的一部分,我有一个实现IEditableObject的基类,因此当BeginEdit()获取状态快照时,将通过反射为所有可写、非集合属性创建字典

var propertyInfos = GetType().GetWritableNonCollectionPropertyInfos();
var dic = propertyInfos
            .ToDictionary(pi => pi.Name, pi => pi.GetValue(this, null));
此词典是IsDirty跟踪和回滚CancelEdit()的基础

默认行为排除集合的原因是,它们通常是不必要的,枚举起来可能很昂贵,而且通常是一种痛苦

关于这个问题,我有一个用例,其中集合状态是必需的。它是一组自定义对象,一次很少有超过5个的对象。从上面的代码中可以看到,集合的值最终将成为集合本身的引用;它会随着集合的变化而变化,因此您无法判断它是否真的很脏

因此,我需要为集合存储一些值,这些值将是不可变的,并且在集合更改时与集合的值相比较。以下是我的大致想法:

  • 提供受保护的IncludeCollections标志
  • 当标志为true时,为子类提供一个模板方法,以仅返回集合属性的其他键值对

        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());
    }
    返回散列
    }
    
  • 这看起来确实有效,但是。。。太复杂了!有没有人看到一种更简单和/或更有效的方法来跟踪收集状态

    这个问题的要点是可以回答的,GetImmutableCollectionValue方法看起来像是一个为集合获取不可变值的好的通用算法吗

    干杯

    解决办法 冒着进一步冒犯“警察无法回答这个问题”的风险。。。这里有一个解决方案

    是的,我认为通用算法很有用,我把它作为扩展添加了进去。我添加了另一个扩展,以加快和简化基于具有DisplayName属性的域超类的集合的过程:

        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。硬币的另一面是,上下文和解决方案动机可能没有被理解。我确实以一句话结束了最初的帖子