C# 如何选择IList或IEnumerable作为方法参数类型

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

我正在编写一个库,并试图确定用于方法参数的最佳类型。我还试图确定在过于挑剔和获取write方法定义之间的界限

这里有一些代码

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的计数方法将使用