【问题标题】:KeyedCollection with MutableKey带有 MutableKey 的 KeyedCollection
【发布时间】:2018-02-08 05:13:44
【问题描述】:

我从this article开始获取不同类型的MutableKey集合。

我想为我的所有集合创建一个抽象的 KeyedCollection 基类,因此我使用泛型和接口来实现我的目的,但我想知道是否有更优雅的解决方案来避免暴露 Collections em> 我的物品的属性。

    public class FooItem : IMyKeyedCollectionItem<FooItem>
    {            
        // *** In this way the setter of the Collections property is public, any other solution to avoid this? **//
        public HashSet<MyKeyedCollectionBase<FooItem>> Collections { get; set; } = new HashSet<MyKeyedCollectionBase<FooItem>>();

        private string _name;              
        public string Name
        {
            get { return _name; }
            set
            {
                if (Collections != null)
                {
                    foreach (var collection in Collections)
                    {
                        collection.ChangeKey(this, value);
                    }

                }
                _name = value;
            }
        }
    }

    /// <summary>
    /// Interface for the mutablekey keyedcollection.
    /// </summary>
    /// <typeparam name="T"></typeparam>        
    public interface IMyKeyedCollectionItem<T> where T : IMyKeyedCollectionItem<T>
    {
        /// <summary>
        /// Collections that contain this item.
        /// </summary>
        HashSet<MyKeyedCollectionBase<T>> Collections { get; set; }
    }

    // KeyedCollection is an abstract class, so I have to derive            
    public abstract class MyKeyedCollectionBase<T> : KeyedCollection<string, T> where T : IMyKeyedCollectionItem<T>
    {            
        public MyKeyedCollectionBase() : base(StringComparer.OrdinalIgnoreCase, 0) { } // case-insensitive

        public MyKeyedCollectionBase(MyKeyedCollectionBase<T> collection)
        {
            if (collection != null)
            {
                foreach (var item in collection)
                    Add(item);
            }
        }            

        protected override void InsertItem(int index, T item)
        {
            base.InsertItem(index, item);

            AddCollectionToItem(item);
        }

        private void AddCollectionToItem(T item)
        {
            if (item.Collections == null)
                item.Collections = new HashSet<MyKeyedCollectionBase<T>>();

            item.Collections.Add(this);
        }

        private void RemoveCollectionFromItem(T item)
        {
            item.Collections.Remove(this);

            if (item.Collections.Count == 0)                
                item.Collections = null;                                    
        }

        protected override void SetItem(int index, T item)
        {
            var replaced = Items[index];
            base.SetItem(index, item);
            AddCollectionToItem(item);
            RemoveCollectionFromItem(replaced);
        }

        protected override void RemoveItem(int index)
        {
            var removedItem = Items[index];
            base.RemoveItem(index);
            RemoveCollectionFromItem(removedItem);
        }

        protected override void ClearItems()
        {
            foreach (var removed in Items)
                RemoveCollectionFromItem(removed);

            base.ClearItems();
        }

        // Expose this method internally to allow mutable item keys: When the key for an item changes, this method is used to change the key in the lookup dictionary
        internal virtual void ChangeKey(T item, string newKey)
        {
            base.ChangeItemKey(item, newKey);
        }            
    }

    public class MyFooKeyedCollection : MyKeyedCollectionBase<FooItem>
    {
        protected override string GetKeyForItem(FooItem item)
        {
            return item.Name;
        }
    }

【问题讨论】:

  • codereview.stackexchange.com 可能感兴趣。
  • 两件事。你总是打电话给ChangeKey,即使_name == value 似乎没有必要。其次,FooItem“知道”它所在的集合,这不应该是它的责任。如何实现INotifyPropertyChanged 并让集合为它包含的每个对象注册到该事件,然后在任何时候为引发该事件的任何对象适当地调用ChangeKey。 -- 这样,集合单独负责保存和维护对象,而对象保持不被遗忘。
  • 嗨@Corak,很好的提示,我详细说明了它,看起来很好。

标签: c# .net keyedcollection


【解决方案1】:

感谢@Corak,我找到了方法...

public class KeyChangedEventArgs : PropertyChangedEventArgs
    {
        public virtual string NewKey { get; }

        public KeyChangedEventArgs(string propertyName, string newKey) : base(propertyName)
        {
            NewKey = newKey;
        }
    }

    public delegate void KeyChangedEventHandler(object sender, KeyChangedEventArgs e);

    public interface INotifyKeyChanged
    {
        event KeyChangedEventHandler KeyChanged;
    }

    /// <summary>
    /// Interface for the mutablekey keyedcollection.
    /// </summary>
    /// <typeparam name="T"></typeparam>        
    public interface IMyKeyedCollectionItem<T> : INotifyKeyChanged where T : IMyKeyedCollectionItem<T>
    {
        /// <summary>
        /// Gets the key for the item of the collection.
        /// </summary>
        /// <returns>The item key.</returns>
        string GetKey();
    }

    public class FooItem : IMyKeyedCollectionItem<FooItem>
    {
        public FooItem(string name)
        {
            Name = name;
        }                        

        private string _name;              
        public string Name
        {
            get { return _name; }
            set
            {
                if (!String.Equals(_name, value, StringComparison.OrdinalIgnoreCase)) // case-insensitive
                {                        
                    // The key on the KeyedCollection must be changed before changing the key on the item.
                    OnKeyChanged(value);

                    _name = value;                        
                }
            }
        }

        public event KeyChangedEventHandler KeyChanged;

        [NotifyPropertyChangedInvocator]
        protected virtual void OnKeyChanged(string newKey, [CallerMemberName] string propertyName = null)
        {
            KeyChanged?.Invoke(this, new KeyChangedEventArgs(propertyName, newKey));
        }

        public string GetKey()
        {
            return Name;
        }
    }

    // KeyedCollection is an abstract class, so I have to derive            
    public abstract class MyKeyedCollectionBase<T> : KeyedCollection<string, T> where T : IMyKeyedCollectionItem<T>
    {            
        public MyKeyedCollectionBase() : base(StringComparer.OrdinalIgnoreCase, 0) { } // case-insensitive

        public MyKeyedCollectionBase(MyKeyedCollectionBase<T> collection)
        {
            if (collection != null)
            {
                foreach (var item in collection)
                    Add(item);
            }
        }            

        protected override void InsertItem(int index, T item)
        {
            base.InsertItem(index, item);

            SubscribeKeyChanged(item);
        }

        private void SubscribeKeyChanged(T item)
        {                
            ((INotifyKeyChanged)item).KeyChanged += OnItemKeyChanged;
        }

        private void OnItemKeyChanged(object sender, KeyChangedEventArgs e)
        {
            var item = (T) sender;
            ChangeKey(item, e.NewKey);
        }

        private void UnsubscribeKeyChanged(T item)
        {
            ((INotifyKeyChanged)item).KeyChanged -= OnItemKeyChanged;                
        }

        protected override void SetItem(int index, T item)
        {
            var replaced = Items[index];
            base.SetItem(index, item);
            SubscribeKeyChanged(item);
            UnsubscribeKeyChanged(replaced);
        }

        protected override void RemoveItem(int index)
        {
            var removedItem = Items[index];
            base.RemoveItem(index);
            UnsubscribeKeyChanged(removedItem);
        }

        protected override void ClearItems()
        {
            foreach (var removed in Items)
                UnsubscribeKeyChanged(removed);

            base.ClearItems();
        }

        // Expose this method internally to allow mutable item keys: When the key for an item changes, this method is used to change the key in the lookup dictionary
        internal virtual void ChangeKey(T item, string newKey)
        {
            base.ChangeItemKey(item, newKey);
        }            
    }

    public class MyFooKeyedCollection : MyKeyedCollectionBase<FooItem>
    {
        protected override string GetKeyForItem(FooItem item)
        {
            return item.Name;
        }
    }

【讨论】:

    猜你喜欢
    • 2016-07-08
    • 2013-05-20
    • 2015-12-05
    • 1970-01-01
    • 1970-01-01
    • 2010-10-30
    • 2011-12-27
    • 1970-01-01
    • 2011-03-10
    相关资源
    最近更新 更多