如果在.Last()
上调用了IList
扩展方法吗?我只是想知道这些之间是否存在显着的性能差异:
IList<int> numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
int lastNumber1 = numbers.Last();
int lastNumber2 = numbers[numbers.Count-1];
Intuition告诉我第一个选择是O(n),但第二个是O(1)。 .Last()
“智能”足以尝试将其投射到IList
吗?
答案 0 :(得分:19)
可能不是,因为它可以做list[list.count-1]
反射器验证:
public static TSource Last<TSource>(this IEnumerable<TSource> source)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
IList<TSource> list = source as IList<TSource>;
if (list != null)
{
int count = list.Count;
if (count > 0)
{
return list[count - 1];
}
}
...
}
答案 1 :(得分:6)
这是未记录的优化,但Enumerable.Last
的无谓词重载确实会直接跳到最后。
请注意,使用谓词的重载不仅仅是从最后开始,正如您所期望的那样向前工作 - 它从一开始就向前发展。我相信这是为了避免在谓词可能抛出异常(或引起其他副作用)时出现不一致。
有关详细信息,请参阅我的blog post about implementing First/Last/Single etc,以及在Single / SingleOrDefault的重载之间存在 的不一致。
答案 2 :(得分:3)
反射器:
public static TSource Last<TSource>(this IEnumerable<TSource> source)
{
...
if (list != null)
{
int count = list.Count;
if (count > 0)
{
return list[count - 1];
}
}
else
{
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
...
}
}
throw Error.NoElements();
}
答案 3 :(得分:1)
答案:是的。
这是一个很好的方法来找出:
class MyList<T> : IList<T> {
private readonly List<T> list = new List<T>();
public T this[int index] {
get {
Console.WriteLine("Inside indexer!");
return list[index];
}
set {
list[index] = value;
}
}
public void Add(T item) {
this.list.Add(item);
}
public int Count {
get {
Console.WriteLine("Inside Count!");
return this.list.Count;
}
}
// all other IList<T> interface members throw NotImplementedException
}
然后:
MyList<int> list = new MyList<int>();
list.Add(1);
list.Add(2);
Console.WriteLine(list.Last());
输出:
Inside Count!
Inside indexer!
2
如果您尝试这样做:
Console.WriteLine(list.Last(n => n % 2 == 0));
然后你在GetEnumerator
中得到一个异常,表明它正试图走列表。如果我们通过
GetEnumerator
public IEnumerator<T> GetEnumerator() {
Console.WriteLine("Inside GetEnumerator");
return this.list.GetEnumerator();
}
再试一次,我们看到
Inside GetEnumerator!
2
在控制台上显示索引器从未使用过。
答案 4 :(得分:0)
原创海报是在谈论界面,而不是实施。
所以它取决于所讨论的IList/Ilist<T>
背后的底层实现。您不了解其索引器的实现方式。我相信框架的List<T>
有一个利用数组的具体实现,因此可以直接查找,但如果你只有IList<T>
的引用,那么这不是任何给定的。 / p>