IList的 和IReadOnlyList (IList and IReadOnlyLi

2019-08-01 08:55发布

如果我有需要参数的方法,

  • 有一个Count属性
  • 有一个整数索引(GET-只)

我应该这个参数的类型是什么? 我会选择IList<T>之前,.NET 4.5,因为有这个没有其他的可转位采集接口和数组实现它,这是一大利好。

但.NET 4.5引入了新的IReadOnlyList<T>接口,我想我的方法来支持这一点。 我怎么能写这个方法来支持IList<T>IReadOnlyList<T>不违反喜欢干的基本原则是什么?

编辑 :丹尼尔的回答给了我一些建议:

public void Foo<T>(IList<T> list)
    => Foo(list, list.Count, (c, i) => c[i]);

public void Foo<T>(IReadOnlyList<T> list)
    => Foo(list, list.Count, (c, i) => c[i]);

private void Foo<TList, TItem>(
    TList list, int count, Func<TList, int, TItem> indexer)
    where TList : IEnumerable<TItem>
{
    // Stuff
}

编辑2:或者我可以接受IReadOnlyList<T>和提供这样的帮助:

public static class CollectionEx
{
    public static IReadOnlyList<T> AsReadOnly<T>(this IList<T> list)
    {
        if (list == null)
            throw new ArgumentNullException(nameof(list));

        return list as IReadOnlyList<T> ?? new ReadOnlyWrapper<T>(list);
    }

    private sealed class ReadOnlyWrapper<T> : IReadOnlyList<T>
    {
        private readonly IList<T> _list;

        public ReadOnlyWrapper(IList<T> list) => _list = list;

        public int Count => _list.Count;

        public T this[int index] => _list[index];

        public IEnumerator<T> GetEnumerator() => _list.GetEnumerator();

        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    }
}

然后,我可以把它像Foo(list.AsReadOnly())


编辑3:数组实现两者IList<T>IReadOnlyList<T>所以确实的List<T>类。 这使得它非常难得找到一个实现类IList<T>而不是IReadOnlyList<T>

Answer 1:

你的运气了这里。 IList<T>不实现IReadOnlyList<T>List<T>不实现这两个接口,但我认为这不是你想要的。

但是,你可以使用LINQ:

  • Count()扩展方法在内部检查实际上实例是否是一个集合,然后使用Count属性。
  • 所述ElementAt()扩展方法在内部检查实际上实例是否是列表,比使用的索引器。


Answer 2:

如果您更关心的是维持的主要DRY超性能,你可以使用dynamic ,就像这样:

public void Do<T>(IList<T> collection)
{
    DoInternal(collection, collection.Count, i => collection[i]);
}
public void Do<T>(IReadOnlyList<T> collection)
{
    DoInternal(collection, collection.Count, i => collection[i]);
}

private void DoInternal(dynamic collection, int count, Func<int, T> indexer)
{
    // Get the count.
    int count = collection.Count;
}

但是,我不能真诚地说,我建议以此为陷阱实在是太大了:

  • 在每次调用collectionDoInternal会在运行时得到解决。 你失去类型安全,编译时检查等。
  • 性能下降(虽然不严重,为单一的情况,但可以合并计算后) 发生

你的助手的建议是最有用的,但我认为你应该围绕翻转; 鉴于IReadOnlyList<T>接口引入在.NET 4.5,所以很多API的没有对它的支持,但对支持IList<T>接口 。

这就是说,你应该创建一个AsList包装,它接受一个IReadOnlyList<T>并在返回包装IList<T>实施。

但是,如果你想强调你的API,你正在服用的IReadOnlyList<T>强调这样一个事实,你是不是变异的数据),那么AsReadOnlyList ,你现在有扩展是比较合适的,但我D构成如下优化AsReadOnly

public static IReadOnlyList<T> AsReadOnly<T>(this IList<T> collection)
{
    if (collection == null)
        throw new ArgumentNullException("collection");

    // Type-sniff, no need to create a wrapper when collection
    // is an IReadOnlyList<T> *already*.
    IReadOnlyList<T> list = collection as IReadOnlyList<T>;

    // If not null, return that.
    if (list != null) return list;

    // Wrap.
    return new ReadOnlyWrapper<T>(collection);
}


Answer 3:

由于IList<T>IReadOnlyList<T>不共享任何有用的“祖先”,如果你不希望你的方法接受任何其他类型的参数,你能做的唯一的事情就是提供两个重载。

如果您认为重用代码是重中之重,那么你可以有这些重载的呼叫转移到一个private接受方法IEnumerable<T>和方式丹尼尔建议使用LINQ,实际上让LINQ做归一化在运行时。

但恕我直言,它很可能是更好的只是复制/粘贴代码,一次只保留上个参数的类型不同的两种独立的过载; 我不相信这个规模是微架构提供任何有形的,而在另一方面,它要求非显而易见性演习和较慢。



Answer 4:

你需要的是IReadOnlyCollection<T>在.NET 4.5提供这实质上是一个IEnumerable<T>具有Count的财产,但如果你需要建立索引,以及那么你需要IReadOnlyList<T>这也将给出一个索引。

我不知道你,但我觉得这个接口是必须有一个已经失踪了很长一段时间。



文章来源: IList and IReadOnlyList