我经常发现自己在做类似的事情:
string[] things = arrayReturningMethod();
int index = things.ToList<string>.FindIndex((s) => s.Equals("FOO"));
//do something with index
return things.Distinct(); //which returns an IEnumerable<string>
我发现所有这些类型/接口的混合有点令人困惑,它发现了我潜在的性能问题触角(我忽略了它,直到被证明是正确的,当然)。
这是一个惯用且恰当的C#还是有更好的替代方法可以避免来回使用来访问使用数据的正确方法?
编辑: 问题实际上是双重的:
何时直接使用IEnumerable接口或数组或列表(或任何其他IEnumerable实现类型)是正确的(接受参数时)?
你是否可以在IEnumerables(实现未知)和列表以及IEnumerables和数组以及数组和列表之间自由移动,或者是非惯用的(有更好的方法)/非高性能(通常不相关,但可能)在某些情况下)/只是丑陋(不可维护,不可读)?
答案 0 :(得分:7)
一个好的经验法则是始终使用IEnumerable(在声明变量/方法参数/方法返回类型/属性/等时),除非你有充分的理由不这样做。到目前为止,与其他(特别是扩展)方法的类型最兼容。
答案 1 :(得分:7)
关于表现......
关于成语......
通常,IEnumerable对公共属性,函数参数以及返回值通常都很有用 - 并且只有当您知道要按顺序使用值时才会这样。
例如,如果你有一个函数PrintValues,如果它被写为PrintValues(List&lt; T&gt;值),它只能处理List值,所以用户首先必须转换,例如他们正在使用T []。同样,如果函数是PrintValues(T []值)。但如果它是PrintValues(IEnumerable&lt; T&gt;值),它将能够处理Lists,T [],堆栈,哈希表,字典,字符串,集合等 - 任何实现IEnumerable的集合,几乎每个集合。
关于内部使用......
另外,请注意,转换与使用ToArray()或ToList()不同 - 后者涉及复制值,如果您有很多元素,这确实是性能和内存命中。前者只是说“狗是动物,所以像任何动物一样,它可以吃”(垂头丧气)或“这种动物恰好是一只狗,所以它可以吠叫”(向上)。同样,All Lists和T []是IEnumerables,但只有一些IEnumerables是Lists或T [] s。
答案 2 :(得分:3)
嗯,你有两个苹果和橙子,你正在比较。
两个苹果是数组和List。
C#中的数组是一个内置垃圾收集的C风格数组。使用它们的好处是它们的开销很小,假设你不需要移动东西。糟糕的是,当你添加内容,删除内容以及以其他方式更改数组时,它们并不是那么有效,因为内存会被随机移动。
List是C#样式的动态数组(类似于C ++中的vector&lt;&gt;类)。有更多的开销,但是当你需要大量移动时它们会更有效率,因为它们不会试图保持内存使用的连续性。
我能给出的最好的比较是说数组是列表,因为字符串是StringBuilders。
橙色是'IEnumerable'。这不是数据类型,而是接口。当一个类实现IEnumerable接口时,它允许该对象在foreach()循环中使用。
当您返回列表时(就像您在示例中所做的那样),您没有将列表转换为IEnumerable。列表已经是 IEnumerable对象。
编辑:何时在两者之间进行转换:
这取决于应用程序。使用List无法完成的数组可以做的很少,所以我通常会推荐List。可能最好的做法是做出一个你要使用其中一个的设计决定,这样你就不必在两者之间切换。如果您依赖外部库,请将其抽象出来以保持一致的使用。
希望这能清除一点雾。
答案 3 :(得分:1)
在我看来,问题在于你没有费心学习如何搜索阵列。提示:Array.IndexOf或Array.BinarySearch取决于数组是否已排序。
你是正确的,转换到列表是一个坏主意:它浪费空间和时间,使代码可读性降低。另外,盲目向上转换为IEnumerable
会减慢事情的速度并完全阻止使用某些算法(例如二进制搜索)。
答案 4 :(得分:0)
如果可以避免,我会尽量避免在数据类型之间快速跳转。
必须出现的情况是,与您描述的情况类似的每种情况都是完全不同的,以防止关于改变您的类型的教条规则;但是,通常最好选择一种数据结构,尽可能提供所需的接口,而不必将元素不必要地复制到新的数据结构中。
答案 5 :(得分:0)
何时使用?
我建议返回最具体的类型,并采用最灵活的类型。
像这样:
public int[] DoSomething(IEnumerable<int> inputs)
{
//...
}
public List<int> DoSomethingElse(IList<int> inputs)
{
//...
}
这样你就可以在List< T >
上调用方法,除了将它作为IEnumerable处理之外。在输入上,使用尽可能灵活,因此您不要指定您的方法的用户要创建哪种类型的集合。
答案 6 :(得分:-2)
在实际出现性能问题之前,你应该忽略“性能问题”的触角。 大多数性能问题来自于执行太多I / O或过多锁定或其中一个错误,并且这些都不适用于此问题。
我的一般方法是:
#4的推论:不要害怕ToList()。 ToList()是你的朋友。它强制IEnumerable&lt; T&gt;然后进行评估(当你堆叠几个where子句时很有用)。不要坚持下去,但是一旦你建立了完整的where子句,你就可以随意调用它,然后再对它进行预测(或者类似)。
当然,这只是一个粗略的指导方针。请尝试在相同的代码库中遵循相同的模式 - 跳转的代码样式使维护编码人员更难以进入您的思维框架。