【问题标题】:How to turn ICollection<T> into IReadOnlyCollection<T>?如何将 ICollection<T> 变成 IReadOnlyCollection<T>?
【发布时间】:2016-03-25 13:23:39
【问题描述】:

当我在 C# 中有一个 ICollection&lt;T&gt; 变量时,我无法将它传递给需要 IReadOnlyCollection&lt;T&gt; 的函数:

public void Foo()
{
  ICollection<int> data = new List<int>();
  // Bar(data); // Not allowed: Cannot implicitly cast ICollection<int> to IReadOnlyCollection<int>
  Bar(data.ToList()); // Works, since List<T> implements IReadOnlyCollection<T>
}

public void Bar(IReadOnlyCollection<int> data)
{
  if (data.Count == 1) { /* ... */ }
  // ...
}

显然问题在于ICollection&lt;T&gt; 不继承自IReadOnlyCollection&lt;T&gt; - 但为什么呢? ICollection&lt;T&gt; 应该是 IReadOnlyCollection&lt;T&gt; 的完整功能集以及修改集合的函数。

传递参数的最佳解决方案是什么?

一方面,由于我不想更改 Bar 中的集合,只需要计数和迭代集合,因此我需要 IReadOnlyCollection

另一方面,我不想每次调用该函数时都创建一个新的列表对象。

【问题讨论】:

  • 你到底在用那个方法收集什么?只是检查计数?可能有更好的方法来处理这种情况。
  • 当你不希望你的类之外的任何东西修改你的集合时,你真的只使用 ReadOnlyCollection。如果它一开始就不是公共财产,那么担心就变得微不足道了。
  • @M.kazemAkhgary 如果有人通过ICollection 而不是IReadOnlyCollection,这只是在运行时中断。
  • @JeffMercado ICollection&lt;T&gt; 是分层数据结构中的Children(只读)属性,因此允许修改集合是有意的。但是,我不需要在评估树的上下文中修改它们,所以我想将我的功能限制在最严格的界面。 Count == 1 的情况是一种特殊处理,当只有一个子节点时,会跳过整个层级。
  • @Xcalibur37 我认为这有点短视。接受IReadOnlyCollection&lt;T&gt; 的方法表示该方法不会修改集合。

标签: c# icollection


【解决方案1】:

AFAIK 没有标准的解决方案,但像这样制作自己的解决方案并不难

public static class MyExtensions
{
    public static IReadOnlyCollection<T> AsReadOnly<T>(this ICollection<T> source)
    {
        if (source == null) throw new ArgumentNullException("source");
        return source as IReadOnlyCollection<T> ?? new ReadOnlyCollectionAdapter<T>(source);
    }

    sealed class ReadOnlyCollectionAdapter<T> : IReadOnlyCollection<T>
    {
        readonly ICollection<T> source;
        public ReadOnlyCollectionAdapter(ICollection<T> source) => this.source = source;
        public int Count => source.Count;
        public IEnumerator<T> GetEnumerator() => source.GetEnumerator();
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    }
}

然后按如下方式使用

Bar(data.AsReadOnly());

【讨论】:

  • 你会认为这将成为标准的一部分。
【解决方案2】:

在实现IReadOnlyCollection&lt;T&gt; 的同时,您可以非常简单地创建一个组成ICollection&lt;T&gt; 的类。您还可以创建一个扩展方法来进行包装(从而允许泛型类型推断):

public class ReadOnlyCollectionWrapper<T> : IReadOnlyCollection<T>
{
    private ICollection<T> collection;
    public ReadOnlyCollectionWrapper(ICollection<T> collection)
    {
        this.collection = collection;
    }

    public int Count
    {
        get { return collection.Count; }
    }

    public IEnumerator<T> GetEnumerator()
    {
        return collection.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return collection.GetEnumerator();
    }
}

public static class ReadOnlyCollectionWrapper
{
    public static IReadOnlyCollection<T> AsReadOnly<T>(this ICollection<T> collection)
    {
        return new ReadOnlyCollectionWrapper<T>(collection);
    }
}

【讨论】:

  • 谢谢! Ivan Stoev 在下面提出了完全相同的建议。我接受了他的回答,因为他快了 2 分钟。希望你能理解。
猜你喜欢
  • 1970-01-01
  • 2017-06-10
  • 2013-06-28
  • 1970-01-01
  • 1970-01-01
  • 2015-03-12
  • 1970-01-01
  • 2010-12-05
  • 2019-02-08
相关资源
最近更新 更多