0

在父类中,我有一个集合。在子类中,我想公开父类集合的一部分。我希望任何一个位置的更改都会影响另一个位置。

我的真实生活情况是我正在创建将记录数据库设计的应用程序的一部分。我在 Database 类中有一个 ConstraintList 集合。ConstraintList 包含数据库中每个约束的 Constraint 类。我在 Database 类中还有一个 TablesList 集合,其中包含 Table 类。在 Table 类中,我有一个 ForeignKeyConstraintList,我想在其中公开来自父(数据库类) ConstraintList 的约束,这些约束是该 Table 类的外键约束。

+-Database Class
|                            
+--ConstraintList  <-----------
|                             |
+--TableList              Same List
   |                          |
   +-Table Class              |
     |                        |
     +-ForeignKeyConstraintList

我尝试使用主集合中的现有 List 类并使用 Linq 将其过滤到另一个 List 集合。但是这不起作用,因为这会产生两个 List 类。如果一个条目从一个列表中删除,它仍然存在于另一个列表中。

我想过让 Table 类的 ForeignKeyConstraintList 属性在每次被调用时直接从 Database 类的 ConstraintList 属性中提取,但是过滤它的行为会导致它创建一个新的 List 类,因此从 ForeignKeyConstraintList 中删除的任何条目都不会从约束列表中删除。

到目前为止,我想出的另一个选择是创建一个新类,该类实现与 List 相同的接口,但不继承它的子类。然后使用私有字段来存储对主 List 类的引用。然后编写自定义的 Add 和 Remove 方法,将任何更改同步回 ConstraintList。我还需要创建 IEnemerable 和 IEnumerable 的自定义实现来跳过不符合过滤条件的项目。

在父类中,我有一个集合。在子类中,我想公开父类集合的一部分。我希望任何一个位置的更改都会影响另一个位置。

4

1 回答 1

0

我决定编写一个自定义 List 类型的类来实现这一点。我将在下面发布代码。我还没有测试过,但我认为这对于其他想要做同样事情的人来说是一个好的开始。

嗯,看来这个班级太大了,不适合在这里。我将仅发布关键部分并跳过仅实现各种接口的公共方法。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;

namespace CodeWriter.Collections.Generic
{
    /// <summary>
    /// This represents a strongly typed list of objects that can be accessed by index. Provides methods to search, sort and manipulate the list.
    /// This class serves as a wrapper for a <see cref="List{T}"/>.  The internal class can be reached by the <see cref="SourceList"/> property.
    /// The elements that this class exposes from the <see cref="SourceList"/> can be controlled by changing the <see cref="Filter"/> property.
    /// </summary>
    /// <typeparam name="T">The type of elements in the list.</typeparam>
    /// <remarks>
    /// This class was created to support situations where the functionality of two or more <see cref="List{T}"/> collections are needed where one is the Master Collection
    /// and the others are Partial Collections.  The Master Collection is a <see cref="List{T}"/> and exposes all elements in the collection.  The Partial Collections 
    /// are <see cref="FilteredList{T}"/> classes (this class) and only expose the elements chosen by the <see cref="FilteredList{T}"/> property of this class.  When elements are modified,
    /// in either type of collection, the changes show up in the other collections because in the backend they are the same list.  When elements are added or deleted from the Partial Collections,
    /// they will disappear from the Master Collection.  When elements are deleted from the Master Collection, they will not be available in the Partial Collection but it 
    /// may not be apparent because the <see cref="Filter"/> property may not be exposing them.
    /// </remarks>
    public class FilteredList<T> : IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>
    {
        #region Public Constructor
        public FilteredList(List<T> SourceList)
        {
            if (SourceList == null)
            {
                throw new ArgumentNullException("SourceList");
            }

            _SourceList = SourceList;
        }

        public FilteredList()
        {
            _SourceList = new List<T>();
        }

        public FilteredList(IEnumerable<T> Collection)
        {
            if (Collection == null)
            {
                throw new ArgumentNullException("Collection");
            }

            _SourceList = new List<T>(Collection);
        }
        #endregion

        #region Protected Members
        protected List<T> _SourceList;
        protected Func<T, bool> _Filter;
        #endregion

        #region Public Properties
        #region Source List Properties
        /// <summary>
        /// Gets or sets the base class that this class is a wrapper around.
        /// </summary>
        public List<T> SourceList
        {
            get
            {
                return _SourceList;
            }
            set
            {
                _SourceList = value;
            }
        }

        /// <summary>
        /// Gets or sets the value used to filter the <see cref="SourceList"/>.
        /// </summary>
        public Func<T, bool> Filter
        {
            get
            {
                return _Filter;
            }
            set
            {
                _Filter = value;
            }
        }
        #endregion
        #region Normal List<T> Implementation
        /// <summary>
        /// Provides access to the collection the in the same manner as an <see cref="Array"/>.
        /// </summary>
        /// <param name="Index">The Index of the element you want to retrieve.  Valid values are from zero to the value in the <see cref="Count"/> property.</param>
        /// <returns>The element at the position provided with the indexer.</returns>
        public T this[int Index]
        {
            get
            {
                List<T> Selected = _SourceList.Where(_Filter).ToList();
                return Selected[Index];
            }
            set
            {
                List<T> Selected = _SourceList.Where(_Filter).ToList();
                Selected[Index] = value;
            }
        }

        /// <summary>
        /// Provides access to the collection the in the same manner as an <see cref="Array"/>.
        /// </summary>
        /// <param name="Index">The Index of the element you want to retrieve.  Valid values are from zero to the value in the <see cref="Count"/> property.</param>
        /// <returns>The element at the position provided with the indexer.</returns>
        /// <remarks>This is required for IList implementation.</remarks>
        object IList.this[int Index]
        {
            get
            {
                return this[Index];
            }
            set
            {
                if ((value is T) == false)
                {
                    throw new ArgumentException("Value passed is not a valid type.");
                }

                this[Index] = (T)value;
            }
        }

        /// <summary>
        /// Gets or sets the total number of elements the internal data structure can hold without resizing.
        /// </summary>
        public int Capacity
        {
            get
            {
                return _SourceList.Capacity;
            }
            set
            {
                // We cannot let them shrink capacity because this class is a wrapper for the List<T> in the _SourceList property.
                //  They don't get to see all the entries in that list because it is filtered.  Therefore it is not safe for them to shrink capacity.

                // We check if they are shrinking the capacity.
                if (value >= _SourceList.Capacity)
                {
                    _SourceList.Capacity = value;
                }
            }
        }

        /// <summary>
        /// Gets the number of elements contained in the <see cref="FilteredList{T}"/>.
        /// </summary>
        public int Count
        {
            get
            {
                List<T> Selected = _SourceList.Where(_Filter).ToList();

                return Selected.Count();
            }
        }

        /// <summary>
        /// Gets a value indicating whether the <see cref="FilteredList{T}"/> has a fixed size.
        /// </summary>
        public bool IsFixedSize
        {
            get
            {
                return false;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the <see cref="FilteredList{T}"/> is read-only.
        /// </summary>
        public bool IsReadOnly
        {
            get
            {
                return false;
            }
        }

        /// <summary>
        /// Gets a value indicating whether access to the <see cref="FilteredList{T}"/> is synchronized (thread safe).
        /// </summary>
        public bool IsSynchronized
        {
            get
            {
                return false;
            }
        }

        /// <summary>
        /// Gets an object that can be used to synchronize access to the <see cref="FilteredList{T}"/>.
        /// </summary>
        public object SyncRoot
        {
            get
            {
                return _SourceList;
            }
        }
        #endregion
        #endregion
    }
}
于 2019-10-31T06:06:39.683 回答