C# IList<T> 和 IReadOnlyList<T>
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12838122/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
IList<T> and IReadOnlyList<T>
提问by ?afak Gür
If I have a method that requires a parameter that,
如果我有一个方法需要一个参数,
- Has a
Countproperty - Has an integer indexer (get-only)
- 有
Count房产 - 有一个整数索引器(仅限获取)
What should the type of this parameter be? I would choose IList<T>before .NET 4.5 since there was no other indexable collection interface for this and arrays implement it, which is a big plus.
这个参数的类型应该是什么?我会IList<T>在 .NET 4.5 之前选择,因为没有其他可索引的集合接口,而且数组实现了它,这是一个很大的优势。
But .NET 4.5 introduces the new IReadOnlyList<T>interface and I want my method to support that, too. How can I write this method to support both IList<T>and IReadOnlyList<T>without violating the basic principles like DRY?
但是 .NET 4.5 引入了新IReadOnlyList<T>接口,我希望我的方法也支持它。我如何编写这种方法来支持IList<T>并且IReadOnlyList<T>不违反 DRY 等基本原则?
Edit: Daniel's answer gave me some ideas:
编辑:丹尼尔的回答给了我一些想法:
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
}
Edit 2:Or I could just accept an IReadOnlyList<T>and provide a helper like this:
编辑 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();
}
}
Then I could call it like Foo(list.AsReadOnly())
然后我可以称之为 Foo(list.AsReadOnly())
Edit 3:Arrays implement both IList<T>and IReadOnlyList<T>, so does the List<T>class. This makes it pretty rare to find a class that implements IList<T>but not IReadOnlyList<T>.
编辑 3:数组同时实现IList<T>和IReadOnlyList<T>,List<T>类也是如此。这使得很难找到实现IList<T>但不实现的类IReadOnlyList<T>。
采纳答案by Daniel Hilgarth
You are out of luck here. IList<T>doesn't implement IReadOnlyList<T>. List<T>does implement both interfaces, but I think that's not what you want.
你在这里倒霉了。IList<T>不执行IReadOnlyList<T>。List<T>确实实现了两个接口,但我认为这不是你想要的。
However, you can use LINQ:
但是,您可以使用 LINQ:
- The
Count()extension method internally checks whether the instance in fact is a collection and then uses theCountproperty. - The
ElementAt()extension method internally checks whether the instance in fact is a list and than uses the indexer.
- 该
Count()扩展方法内部检查其实实例是否是一个集合,然后使用Count属性。 - 该
ElementAt()扩展方法内部检查其实实例是否是列表,比使用索引。
回答by Jon
Since IList<T>and IReadOnlyList<T>do not share any useful "ancestor", and if you don't want your method to accept any other type of parameter, the only thing you can do is provide two overloads.
由于IList<T>并且IReadOnlyList<T>不共享任何有用的“祖先”,并且如果您不希望您的方法接受任何其他类型的参数,那么您唯一可以做的就是提供两个重载。
If you decide that reusing codes is a top priority then you could have these overloads forward the call to a privatemethod that accepts IEnumerable<T>and uses LINQ in the manner Daniel suggests, in effect letting LINQ do the normalization at runtime.
如果您认为重用代码是重用,那么您可以让这些重载将调用转发到一个private方法,IEnumerable<T>该方法以 Daniel 建议的方式接受和使用 LINQ,实际上是让 LINQ 在运行时进行规范化。
However IMHO it would probably be better to just copy/paste the code once and just keep two independent overloads that differ on just the type of argument; I don't believe that micro-architecture of this scale offers anything tangible, and on the other hand it requires non-obvious maneuvers and is slower.
但是恕我直言,最好只复制/粘贴一次代码并保留两个仅在参数类型上不同的独立重载;我不相信这种规模的微架构提供任何有形的东西,另一方面,它需要不明显的操作并且速度较慢。
回答by casperOne
If you're more concerned with maintaining the principal of DRYover performance, you coulduse dynamic, like so:
如果您更关心维护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;
}
However, I can't say in good faith that I'd recommend this as the pitfalls are too great:
但是,我不能真诚地说,我建议以此为陷阱实在是太大了:
- Every call on
collectioninDoInternalwill be resolved at run time. You lose type safety, compile-time checks, etc. - Performance degradation (while not severe, for the singular case, but can be when aggregated) willoccur
- 每次调用
collectioninDoInternal都将在运行时解决。你失去了类型安全、编译时检查等。 - 性能下降(虽然不严重,对于单一情况,但在聚合时可能会发生)
Your helper suggestion is the most useful, but I think you should flip it around; given that the IReadOnlyList<T>interfacewas introduced in .NET 4.5, many API's don't have support for it, but have support for the IList<T>interface.
你的助手建议是最有用的,但我认为你应该把它翻过来;鉴于该IReadOnlyList<T>接口是在 .NET 4.5 中引入的,许多 API 不支持它,但支持该IList<T>接口。
That said, you should create an AsListwrapper, which takes an IReadOnlyList<T>and returns a wrapper in an IList<T>implementation.
也就是说,您应该创建一个AsList包装器,它IReadOnlyList<T>在IList<T>实现中接受并返回一个包装器。
However, if you want to emphasize on your API that you are taking an IReadOnlyList<T>(to emphasize the fact that you aren't mutating the data), then the AsReadOnlyListextension that you have now would be more appropriate, but I'd make the following optimization to AsReadOnly:
但是,如果您想强调您正在使用的 API IReadOnlyList<T>(强调您没有改变数据的事实),那么AsReadOnlyList您现在拥有的扩展会更合适,但我会进行以下优化到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);
}
回答by MaYaN
What you need is the IReadOnlyCollection<T>available in .Net 4.5 which is essentially an IEnumerable<T>which has Countas the property but if you need indexing as well then you need IReadOnlyList<T>which would also give an indexer.
你需要的是IReadOnlyCollection<T>在.NET 4.5可用其本质上是一个IEnumerable<T>具有Count作为属性,但如果你需要索引以及那么你需要IReadOnlyList<T>这也将给出一个索引。
I don't know about you but I think this interface is a must have that had been missing for a very long time.
我不了解你,但我认为这个界面是必不可少的,但它已经消失了很长时间。

