C# 自定义INotifyCollectionChanged - 实现集合唯一性

11 篇文章 0 订阅

INotifyCollectionChanged

详见: [INotifyCollectionChanged 接口](https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.specialized.inotifycollectioncha nged?view=net-5.0).

    //
    // 摘要:
    //     例如,当添加和删除项或清除整个列表时,向侦听器通知动态更改。
    [TypeForwardedFrom("WindowsBase, Version=3.0.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
    public interface INotifyCollectionChanged
    {
        //
        // 摘要:
        //     当集合更改时发生。
        event NotifyCollectionChangedEventHandler CollectionChanged;
    }

UniquenessCollection

    [Serializable]
    public class UniquenessCollection<T, TKey> : ICollection<T>, INotifyCollectionChanged, INotifyPropertyChanged
    {
        private List<T> m_ListCache = new List<T>();
        private readonly IComparer<TKey> m_Comparer = Comparer<TKey>.Default;

        private readonly Func<T, TKey> m_Func;

        public UniquenessCollection(Func<T, TKey> uniquenessKeyFunc)
        {
            this.m_Func = uniquenessKeyFunc ?? throw new ArgumentNullException(nameof(uniquenessKeyFunc));
        }

        public int Count => this.m_ListCache.Count;

        public bool IsReadOnly { get; set; } = false;

        public T this[int index] => this.m_ListCache[index];

        public ReadOnlyCollection<T> AsReadOnly()
        {
            return this.m_ListCache.AsReadOnly();
        }

        public IList<T> Items => this.m_ListCache;

        public ReadOnlyCollection<TKey> Keys => this.Select(this.GetKey).AsReadOnly();

        public bool Add(T item)
        {
            bool canAdd = this.CanAdd(item);
            if (canAdd)
            {
                this.m_ListCache.Add(item);
                this.RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, this.m_ListCache.Count - 1));
            }
            return canAdd;
        }

        public virtual bool CanAdd(T item)
        {
            if (this.IsReadOnly)
            {
                return false;
            }
            object temp = this.GetKey2Obj(item);
            return this.m_ListCache.FirstOrDefault(x => this.GetKey2Obj(x) == temp) == null;
        }

        public bool Contains(TKey key)
        {
            return this.m_ListCache.FirstOrDefault(x => this.m_Comparer.Compare(this.GetKey(x), key) == 0) != null;
        }

        public TKey GetKey(T item)
        {
            return this.m_Func.Invoke(item);
        }

        private object GetKey2Obj(T item)
        {
            return this.m_Func.Invoke(item);
        }

        public T Find(Predicate<T> match)
        {
            return match == null ? default : this.FirstOrDefault(x => match(x));
        }

        public bool Exist(Predicate<T> match)
        {
            if (match == null)
            {
                return false;
            }
            return this.FirstOrDefault(x => match(x)) != null;
        }
        #region ICollection<T>

        void ICollection<T>.Add(T item)
        {
            this.Add(item);
        }

        public void Clear()
        {
            List<T> oldList = this.m_ListCache;
            this.m_ListCache = new List<T>();
            this.RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, oldList, 0));
        }

        public bool Contains(T item)
        {
            return this.m_ListCache.Contains(item);
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            this.m_ListCache.CopyTo(array, arrayIndex);
        }

        public bool Remove(T item)
        {
            if (!this.IsReadOnly && this.m_ListCache.Contains(item))
            {
                int index = this.m_ListCache.IndexOf(item);
                if (this.m_ListCache.Remove(item))
                {
                    this.RaiseCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
                    return true;
                }
            }
            return false;
        }
        #endregion

        #region IEnumerable<T>
        public IEnumerator<T> GetEnumerator()
        {
            return this.m_ListCache.GetEnumerator();
        }
        #endregion

        #region IEnumerable
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.m_ListCache.GetEnumerator();
        }
        #endregion

        #region INotifyCollectionChanged

        public event NotifyCollectionChangedEventHandler CollectionChanged;

        public void RaiseCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            if (e != null)
            {
                if (e.NewItems != null)
                {
                    this.OnAddNewItems(e.NewItems.Cast<T>().ToList());
                }

                if(e.OldItems !=null)
                {
                    this.OnRemoveItems(e.OldItems.Cast<T>().ToList());
                }
            }
            this.CollectionChanged?.Invoke(this, e);
            this.RaisePropertyChanged(nameof(this.Count));
            this.RaisePropertyChanged(nameof(this.Keys));
            this.RaisePropertyChanged("Items[]");
        }

        protected virtual void OnRemoveItems(IList<T> oldItems)
        {

        }

        protected virtual void OnAddNewItems(IList<T> newItems)
        {

        }
        #endregion

        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;

        public void RaisePropertyChanged([CallerMemberName] string name = "")
        {
            this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
        #endregion
    }

KeyOfStringUniquenessCollection

    public class KeyOfStringUniquenessCollection<T> : UniquenessCollection<T, string>
    {
        public KeyOfStringUniquenessCollection(Func<T, string> uniquenessKeyFunc) : base(uniquenessKeyFunc)
        {

        }

        public T this[string strKey] => this.FirstOrDefault(x => this.GetKey(x) == strKey);

        public bool InternalAdd(T t)
        {
            bool success = this.Add(t);

            if (!success)
            {
                DNService.MessageService.ShowError($"创建{typeof(T).Name}节点失败,未能将节点添加到{this.GetType().Name}中");
                throw new PlcCreateNodeException($"创建{typeof(T).Name}节点失败,未能将节点添加到{this.GetType().Name}中");
            }
            return success;
        }

        public virtual string VerifyName(string sourceName, out bool isDifferent)
        {
            isDifferent = false;
            if (this.Count <= 0)
            {
                return sourceName;
            }

            string tempName = sourceName;
            for (int i = 1; ; ++i)
            {
                if (!this.Contains(tempName))
                {
                    break;
                }
                tempName = sourceName + i;
            }
            isDifferent = tempName != sourceName;
            return tempName;
        }

        public virtual string VerifyName(string sourceName, string[] exceptNames, out bool isDifferent)
        {
            isDifferent = false;
            if (this.Count <= 0)
            {
                return sourceName;
            }
            List<string> keys = this.Keys.ToList();
            if (exceptNames != null && exceptNames.Length > 0)
            {
                exceptNames.ForEach(x => keys.Remove(x));
            }

            string tempName = sourceName;
            for (int i = 1; ; ++i)
            {
                if (!keys.Contains(tempName))
                {
                    break;
                }
                tempName = sourceName + i;
            }
            isDifferent = tempName != sourceName;
            return tempName;
        }

        public string TryGetName(string sourceName, string defaultName = "", bool throWhenIsDifferent = false)
        {
            string newName = "";

            if (string.IsNullOrWhiteSpace(sourceName))
            {
                newName = this.VerifyName(string.IsNullOrWhiteSpace(defaultName) ? typeof(T).Name : defaultName, out _);
            }
            else
            {
                newName = this.VerifyName(sourceName, out bool isDifferent);
                if (isDifferent)
                {
                    if (throWhenIsDifferent)
                    {
                        throw new NameConflictException($"创建{typeof(T).Name}时名称冲突!").SetValue(sourceName, newName);
                    }
                    else
                    {
                        DNService.MessageService.ShowWarning($"创建{typeof(T).Name}时名称冲突!,已将名称{sourceName}改为{newName}");
                    }
                }
            }
#if DEBUG
            newName.ThrowIfIsNullOrWhiteSpace();
            ExceptionExtensions.ThrowInvalidOperationException(() => this.Contains(newName));
#endif
            return newName;
        }
    }

ExceptionExtensions

    public static class ExceptionExtensions
    {
        public static void ThrowIfIsNullOrWhiteSpace(this string value, string message = "")
        {
            if (string.IsNullOrWhiteSpace(value))
            {
                throw new System.ArgumentNullException($"Message:{message}");
            }
        }

        public static void ThrowIfIsNull<T>(this T value, string message = "")
        {
            if (value == null)
            {
                throw new ArgumentNullException($"Type:{typeof(T).FullName},Message:{message}");
            }
        }

        public static void ThrowArgumentException(Func<bool> func, string message)
        {
            func.ThrowIfIsNull();
            if (func.Invoke())
            {
                throw new ArgumentException(message);
            }
        }

        public static void ThrowInvalidOperationException(Func<bool> func, string message = "")
        {
            func.ThrowIfIsNull();
            if (func.Invoke())
            {
                throw new InvalidOperationException(message);
            }
        }

        public static void ThrowArgumentOutOfRangeException(Func<bool> func, string message = "")
        {
            func.ThrowIfIsNull();
            if (func.Invoke())
            {
                throw new ArgumentOutOfRangeException(message);
            }
        }
    }

UniquenCacheNodeCollection

    /// <summary>
    /// <see cref="CacheNode"/>的一个集合,具有名称唯一性
    /// </summary>
    [Serializable]
    public class UniquenCacheNodeCollection : KeyOfStringUniquenessCollection<CacheNode>
    {

        private readonly bool m_CanBoth;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="canBoth">是否可以同时存在地址和标签值</param>
        public UniquenCacheNodeCollection(bool canBoth = true) : base((n) => n.Name)
        {
            this.m_CanBoth = canBoth;
        }

        public override bool CanAdd(CacheNode item)
        {
            if (this.m_CanBoth)
            {
                return base.CanAdd(item);
            }

            bool canAdd = true;

            if (this.Count > 0)
            {
                canAdd = this[0].PlcValueType == item.PlcValueType;

                if (!canAdd)
                {
#if DEBUG
                    DNService.MessageService.ShowWarning($"无法添加{nameof(CacheNode)},因为集合值类型为{this[0].PlcValueType},添加的值类型为{item.PlcValueType}");
#else
                    throw new InvalidOperationException($"无法添加{nameof(CacheNode)},因为集合值类型为{this[0].PlcValueType},添加的值类型为{item.PlcValueType}");
#endif
                }

            }
            return canAdd && base.CanAdd(item);
        }
    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Daniel大妞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值