C# 如何选择IList或IEnumerable作为方法参数类型
我正在编写一个库,并试图确定用于方法参数的最佳类型。我还试图确定在过于挑剔和获取write方法定义之间的界限 这里有一些代码C# 如何选择IList或IEnumerable作为方法参数类型,c#,parameters,C#,Parameters,我正在编写一个库,并试图确定用于方法参数的最佳类型。我还试图确定在过于挑剔和获取write方法定义之间的界限 这里有一些代码 public void MethodTakesIList(IList<MyClass> myClassInstances) { List<List<byte>> byteLists = GetByteLists(myClassInstances.Count); for (int i = 0; i < myClassInst
public void MethodTakesIList(IList<MyClass> myClassInstances) {
List<List<byte>> byteLists = GetByteLists(myClassInstances.Count);
for (int i = 0; i < myClassInstances.Count; i++) {
ProcessBytesAndMyClassInstance(byteLists[i], myClassInstances[i]);
}
}
public void MethodTakesIEnumerable(IEnumerable<MyClass> myClassInstances) {
List<List<byte>> byteLists = GetByteLists(myClassInstances.Count());
int i = 0;
foreach(MyClass instance in myClassInstances) {
ProcessBytesAndMyClassInstance(byteLists[i], instance);
i++;
}
}
public void MethodTakesIList(IList myClassInstances){
List byteLists=GetByteLists(myClassInstances.Count);
for(int i=0;i
在文章中,它指出我“应该始终使用只为您真正使用的方法提供契约的类型”。接受IEnumerable的方法仍然定义循环中使用的索引。如果myClassInstances是一个列表,那么这个索引将指向一个有效的条目,这是否意味着我应该使用接受IList的方法?我现在可以使用IList的索引器了。或者,既然存在一个接受IEnumerable的解决方案,那么既然它支持最多数量的输入,我应该使用它吗
IEnumerable
如果您不确定人们将如何使用API,通常是一种安全的选择。在很多情况下,用户不会拥有IList
对象,尤其是在使用Linq中的内容时,除了列表以外的任何其他集合,例如LinkedList
或队列
例如,如果他们做了someList.Select((x)=>newotherthing(x,“test”)
,使用Where
进行过滤,等等,那么他们将得到一个IEnumerable
。虽然System.Linq
中也有一个方便的myEnumerable.ToList()
转换,但这是他们必须进行的额外转换,对性能有一定影响,可能比您希望避免的转换更糟糕
当然,有时出于性能原因,其他类型更可取,尤其是在处理大量小值(例如,byte[]
)或简化实现(例如用于随机访问或修改数据)时
尽管你可能会从标准类和其他流行的语言库中汲取灵感,但你必须考虑自己的权衡。对于公共库,在考虑二进制兼容性时也必须非常小心,例如,如果返回一个
IEnumerable
,则可以更改实现以使用不同的实际类型,如果您返回一个列表
甚至IList
,那么在不破坏兼容性的情况下,您在技术上可能受到的限制会更大。如果我是您,我会从Microsoft如何实现LINQ中获得灵感。许多能够从使用集合类型中获益的运算符仍然将IEnumerable
作为源数据,但他们只是尝试强制转换到ICollection
,以直接访问索引和其他属性
以(这个稍加修改的)Count
为例:
public static int Count<TSource>(this IEnumerable<TSource> source)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
ICollection<TSource> collection = source as ICollection<TSource>;
if (collection != null)
{
return collection.Count;
}
ICollection collection2 = source as ICollection;
if (collection2 != null)
{
return collection2.Count;
}
int num = 0;
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
num = checked(num + 1);
}
return num;
}
}
公共静态整数计数(此IEnumerable源)
{
if(source==null)
{
抛出新的ArgumentNullException(“源”);
}
ICollection collection=源作为ICollection;
if(集合!=null)
{
返回集合。计数;
}
ICollection collection2=源作为ICollection;
if(collection2!=null)
{
返回集合2.计数;
}
int num=0;
使用(IEnumerator enumerator=source.GetEnumerator())
{
while(枚举数.MoveNext())
{
num=已检查(num+1);
}
返回num;
}
}
如果输入类型可强制转换为集合,则Count
运算符只返回计数;否则,它将遍历列表
这样,您就可以充分利用这两种类型。不要太担心您接受哪种类型,而是更担心性能
List.Count
始终是已知的,而LINQ的IEnumerable Count
方法必须枚举整个集合。您的问题主要基于意见,不适合堆栈溢出。也就是说,您正在阅读的指南是合理的,在您的场景中意味着您应该使用IEnumerable
。始终接受仍然适用于您的实现的限制最少的类型作为输入。因为在您的示例中,您实际上不需要为输入集合编制索引,IEnumerable
就足够了,应该使用IList
。实现涉及某个地方的索引这一事实并不重要。唯一重要的是参数必须如何使用。它是否枚举(循环)它一次?使用IEnumerable
?否则,请使用IReadOnlyList
。这是我的一般规则。它是否总是完美的,否(例如,您可以始终使用IEnumerable
,然后根据需要尝试在方法内部强制转换到IReadOnlyList
或调用ToList
)。但这是一个很好的经验法则。我建议不要使用IList
,因为它不是一个很好的接口,因为它的两个主要实现具有如此不同的属性(即,您可以添加到列表中,但不能添加到数组中)。在你的情况下,我不会像你一样使用Count
——只要根据需要添加到列表中即可。@PeterDuniho也许这是基于观点的,但如果有一些广泛接受的设计原则,而我在问这个问题时却不知道该怎么办?如果答案没有揭示这一点,那么将来我可能知道这个问题主要是基于观点的。@Camiloteriento Linq的计数方法将使用