【问题标题】:ReadOnlyCollection with read-only items带有只读项的 ReadOnlyCollection
【发布时间】:2020-11-14 00:39:09
【问题描述】:

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

我正在考虑创建一个派生自ReadOnlyCollection (class ReadOnlyMyCollection : ReadOnlyCollection<MyItem> class ReadOnlyMyCollection : ReadOnlyCollection<ReadOnlyMyItem>) 的新类,它通过方括号运算符返回只读包装类 (ReadOnlyMyItem) ,但是方括号运算符未标记为虚拟并且返回类型甚至不相同,或者构造函数没有要使用的包装类 (IList<ReadOnlyMyItem>) 的 IList。我应该从头开始构建课程 (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;
    // ...
}

【问题讨论】:

  • 您可以从ReadOnlyMyItem 派生MyItem,将所有设置访问器放入派生类中。
  • @KlausGütter 谢谢你的想法,但是我必须在MyClass 中使用List&lt;ReadOnlyMyItem&gt;,所以当我返回new ReadOnlyCollection&lt;ReadOnlyMyItem&gt;(myItems) 时我不会收到错误消息,对吧?这意味着当我想使用它们时,我总是必须将它们转换回MyItem。也可以在课堂外将物品投射到MyItem

标签: c# readonly-collection


【解决方案1】:

你可以使用IReadOnlyList&lt;T&gt;而不是ReadOnlyCollection&lt;T&gt;吗?

IReadOnlyList&lt;T&gt; 仅提供count 和索引运算符[index](和IEnumerable&lt;T&gt;),但如果您只需要这些,您可以编写如下内容:

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 的接口属性。

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

【讨论】:

  • 为避免客户端通过向下转换获得可写对象,WritableMyItem(和IWritableMyItem,如果使用)可以设为私有或内部。
  • 只要将WritableMyItem 设为私有,正如@KlausGütter 建议的那样,这个解决方案似乎效果很好。我认为我会面临的另一个问题是IReadOnlyList&lt;IReadOnlyMyItem&gt; 是否被转换为IList&lt;IReadOnlyMyItem&gt;,但由于 IList 的类型参数不是协变的,我认为这是不可能的。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-27
  • 1970-01-01
  • 1970-01-01
  • 2021-09-22
  • 2017-01-09
  • 1970-01-01
相关资源
最近更新 更多