我在C#/。NET开发与Visual Studio应用程序。 很多时候ReSharper的,在我的方法的原型,我建议用更通用的人来代替我的输入参数的类型。 例如,名单<>用的IEnumerable <>如果我只使用列表,在我的方法的身体一个foreach。 我可以理解为什么它看起来更加聪明,能写,但我很关心的是性能。 我担心,如果我听ReSharper的我的应用程序的性能将会下降...
有人能准确地解释给我(或多或少)什么是幕后的(即在CLR)当我写:
public void myMethod(IEnumerable<string> list)
{
foreach (string s in list)
{
Console.WriteLine(s);
}
}
static void Main()
{
List<string> list = new List<string>(new string[] {"a", "b", "c"});
myMethod(list);
}
并且是与什么区别:
public void myMethod(List<string> list)
{
foreach (string s in list)
{
Console.WriteLine(s);
}
}
static void Main()
{
List<string> list = new List<string>(new string[] {"a", "b", "c"});
myMethod(list);
}
Answer 1:
你担心性能 - 但你有这种担心任何理由? 我的猜测是,你有没有基准代码在所有。 更高性能的代码替换可读的,干净的代码之前,请务必基准。
在这种情况下,调用Console.WriteLine
将完全主宰反正性能。
虽然我怀疑有可能是使用之间的性能差异理论 List<T>
和IEnumerable<T>
在这里,我怀疑它是在现实世界中的应用程序显著案件的数量微乎其微。
它甚至不是因为如果序列类型被用于许多操作-有一个单一的调用GetEnumerator()
其声明为返回IEnumerator<T>
反正。 作为列表变大,两者在性能上的任何差异将变得更小 ,因为它只会有所有在循环的一开始产生任何影响。
虽然忽略了分析,采取着这样的事情是,你就可以基本编码决定之前, 测量性能。
至于幕后发生的事情 - 你必须挖成的究竟是什么在每种情况下,元数据的深层细节。 我怀疑,在接口的情况下,有重定向的一个额外的水平,至少在理论上-的CLR将不得不在哪里工作在目标对象的类型的虚函数表进行IEnumerable<T>
是,然后调用到适当的方法的码。 在的情况下, List<T>
的JIT会知道正确的偏移量虚函数表下手,没有额外的查找。 这只是基于我的JITting,置信转换,虚函数表稍有些浑浊的理解,以及它们如何适用于接口。 它可能是稍有不妥,但更重要的是它是一个实现细节。
Answer 2:
你不得不看生成的代码是一定的,但在这种情况下,我怀疑有太大的差别。 foreach语句总是运行在一个IEnumerable或IEnumerable<T>
即使你指定List<T>
它仍然必须得到IEnumerable<T>
以迭代。
Answer 3:
在一般情况下,我会说,如果你是更换由一般的味道相当于非通用接口(说IList<>
- > IList<T>
你一定会得到更好的或同等性能。
一个独特的卖点是因为,与Java,.NET不使用类型擦除和支持真正的值类型( struct
)的主要区别之一将是它如何存储例如一个List<int>
内部。 这可能很快成为一个很大的区别取决于如何集中正在使用的列表。
一个新空房禁地合成基准测试结果显示:
for (int j=0; j<1000; j++)
{
List<int> list = new List<int>();
for (int i = 1<<12; i>0; i--)
list.Add(i);
list.Sort();
}
要快3.2倍由比半等效非通用的因子:
for (int j=0; j<1000; j++)
{
ArrayList list = new ArrayList();
for (int i = 1<<12; i>0; i--)
list.Add(i);
list.Sort();
}
免责声明我意识到这个基准是合成的,它实际上并没有集中使用的接口就在那里(而不是直接分派虚方法调用特定类型的)等。然而,它说明了我提出的观点。 不要害怕泛型 (至少不是出于性能的考虑)。
Answer 4:
在一般情况下,增加的灵活性将是值得的,会招致什么次要的性能差异。
Answer 5:
在第一个版本(IEnumerable的),它是更通用的,实际上你说的方法接受实现该接口的任何说法。
第二个版本哟限制的方法来接受sepcific类的类型,这是不是建议。 而且性能大致相同。
Answer 6:
提出这一建议的根本原因是创建于IEnumberable工作与名单是未来的灵活性的方法。 如果将来您需要创建一个MySpecialStringsCollection,你可以有它实现IEnumerable方法,仍然使用相同的方法。
从本质上讲,我认为它下来,除非你发现一个显著的,有意义的性能损失(和我很震惊,如果你注意到的话); 喜欢一个更加宽容的界面,将接受比你现在正在期待些什么。
Answer 7:
为定义List<T>
是:
[SerializableAttribute]
public class List<T> : IList<T>, ICollection<T>,
IEnumerable<T>, IList, ICollection, IEnumerable
所以List<T>
源自IList
, ICollection
, IList<T>,
和ICollection<T>,
除了IEnumerable
和IEnumerable<T>.
所述IEnumerable
接口暴露GetEnumerator
方法,它返回一个IEnumerator
,一个MoveNext
方法,以及Current
特性。 这些机制是什么样的List<T>
类使用通过用foreach和旁边的列表进行迭代。
由此可见,如果IList, ICollection, IList<T>, and ICollection<T>
做的工作不是必需的,那么它的合理使用IEnumerable
或IEnumerable<T>
代替,从而消除了其它管道。
Answer 8:
接口简单地定义由类实现公共方法和属性的存在和签名。 由于接口不“站在自己的”,应该有方法本身,任何“铸造”点球没有性能上的差异-如果有的话-应该差不多也是微不足道的。
Answer 9:
有一个静态上溯造型没有性能损失。 这是一个在程序文本的逻辑结构。
正如其他人所说的,过早的优化是所有罪恶的根源。 编写代码,通过热点分析运行它,你不用担心性能调整以前的事情。
Answer 10:
在获得的IEnumerable <>可能会造成一些麻烦,因为你可能会收到递延执行,或产量回报一些LINQ表达式。 在这两种情况下,你不会有一个集合,但东西你可以反覆。 所以,当你想设定一些界限,你可以要求一个数组。 没有传递参数之前调用collection.ToArray()的一个问题,但你一定会说没有隐藏的不同的注意事项有。
文章来源: Performance impact of changing to generic interfaces