具有只读项的 ReadOnlyCollection

ReadOnlyCollection with read-only items

我有一个 class,其中包含一个集合 (Collection<MyItem> myItems)。我希望能够通过 public 属性(如果可能,在 O(1) 中)私下修改此 Collection 并将其 return 设为只读。我正在考虑使用 ReadOnlyCollection,但问题是项目 (MyItem) 不是只读的。所以我创建了一个只读包装器 class (ReadOnlyMyItem)。如何通过方括号运算符使 ReadOnlyCollection return 包装器 class (ReadOnlyMyItem) 而无需再次构建整个集合,我希望一切都保持 O(1) .

我正在考虑从 ReadOnlyCollectionclass ReadOnlyMyCollection : ReadOnlyCollection<MyItem> class ReadOnlyMyCollection : ReadOnlyCollection<ReadOnlyMyItem>)创建一个新的 class,其中 returns 只读包装器 class (ReadOnlyMyItem) 通过方括号运算符,但方括号运算符未标记为虚拟且 return 类型甚至不是相同或构造函数没有要使用的包装器 class (IList<ReadOnlyMyItem>) 的 IList。我应该从头开始构建 class (ReadOnlyMyCollection) 吗?

代码看起来像这样:

public class MyClass
{
    public ReadOnlyCollection<ReadOnlyMyItem> MyItems { get => GetMyItems(); }
    private List<MyItem> myItems;
    // ...

    private ReadOnlyCollection<ReadOnlyMyItem> GetMyItems()
    {
        throw new NotImplementedException();
    }
    // ...
}

public class MyItem
{
    // ...
}

public class ReadOnlyMyItem
{
    private readonly MyItem myItem;
    // ...

    public ReadOnlyMyItem(MyItem myItem) => this.myItem = myItem;
    // ...
}

你可以使用 IReadOnlyList<T> 而不是 ReadOnlyCollection<T> 吗?

IReadOnlyList<T> 仅提供 count 和索引运算符 [index](和 IEnumerable<T>),但如果这就是您所需要的,您可以这样写:

public interface IReadOnlyMyItem
{
    int SomeValue { get; }
}

public interface IWritableMyItem: IReadOnlyMyItem
{
    new int SomeValue { get;  set; } // Note use of `new`
}

public sealed class WriteableMyItem: IWritableMyItem
{
    public int SomeValue { get; set; }
}

public sealed class MyClass
{
    readonly List<WriteableMyItem> _items = new List<WriteableMyItem>();

    public IReadOnlyList<IReadOnlyMyItem> MyItems() => _items;
}

请注意,我将接口拆分为 IReadOnlyMyItemIWritableMyItem,但您实际上并不需要 IWritableMyItem - 您可以省略该接口并写入:

public sealed class WriteableMyItem: IReadOnlyMyItem
{
    public int SomeValue { get; set; }
}

相反。为了完整起见,我只包含了 IWritableMyItem(因为它展示了如何通过使用 newset 引入到只有 get 的接口 属性 中。

根据下面的评论,您可以通过将 IWriteableMyItemWriteableMyItem 设为私有或内部来防止向下转换的可能性。但是请注意,这仍然可以通过反射来规避 - 但我们正在防止事故而不是欺诈。