所以,我有一个里面有数组的类。目前,我对类的项目进行枚举的策略是使用代码foreach (item x in classInstance.InsideArray)
。我宁愿使用foreach (item x in classInstance)
并将数组设为私有。我主要担心的是我真的需要避免任何缓慢的事情;阵列受到很多打击(并且有几百个项目)。枚举这个数组是很便宜的。一种想法只是让类实现IEnumerable<item>
,但InsideArray.getEnumerator()
只给我一个非通用的枚举器。我也试过实现IEnumerable
接口。这有效但很慢,可能是因为拳击。
有没有办法让类本身可以在没有性能损失的情况下进行枚举?
普通代码:
//Class
public class Foo {
//Stuff
public Item[,] InsideArray {get; private set;}
}
//Iteration. Shows up all over the place
foreach (Item x in classInstance.InsideArray)
{
//doStuff
}
调整后的代码慢得多:
//Class
public class Foo : IEnumerable {
//Stuff
private Item[,] InsideArray;
System.Collections.IEnumerator System.Collections.IEnumerable GetEnumerator()
{
return InsideArray.GetEnumerator();
}
}
//Iteration. Shows up all over the place
foreach (Item x in classInstance)
{
//doStuff
}
注意:为非泛型迭代器添加实现是可能的,并且比我的慢速解决方案更快,但它仍然比直接使用数组更糟糕。我希望有办法以某种方式告诉C#,“嘿,当我要求你迭代这个对象迭代它的数组,同样快,”但显然这是不可能的......至少从建议的答案到目前为止。
答案 0 :(得分:5)
定制迭代器可能会更快(已编辑以已知类型返回):
Basic: 2468ms - -2049509440
Bespoke: 1087ms - -2049509440
(你可以直接使用ArrayIterator作为Foo的GetEnumerator - 本质上是从ArrayEnumerator.GetEnumerator复制代码;我的观点是显示一个类型迭代器比接口更快)
使用代码:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
class Foo
{
public struct ArrayIterator<T> : IEnumerator<T>
{
private int x, y;
private readonly int width, height;
private T[,] data;
public ArrayIterator(T[,] data)
{
this.data = data;
this.width = data.GetLength(0);
this.height = data.GetLength(1);
x = y = 0;
}
public void Dispose() { data = null; }
public bool MoveNext()
{
if (++x >= width)
{
x = 0;
y++;
}
return y < height;
}
public void Reset() { x = y = 0; }
public T Current { get { return data[x, y]; } }
object IEnumerator.Current { get { return data[x, y]; } }
}
public sealed class ArrayEnumerator<T> : IEnumerable<T>
{
private readonly T[,] arr;
public ArrayEnumerator(T[,] arr) { this.arr = arr; }
public ArrayIterator<T> GetEnumerator()
{
return new ArrayIterator<T>(arr);
}
System.Collections.Generic.IEnumerator<T> System.Collections.Generic.IEnumerable<T>.GetEnumerator()
{
return GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public int[,] data;
public IEnumerable<int> Basic()
{
foreach (int i in data) yield return i;
}
public ArrayEnumerator<int> Bespoke()
{
return new ArrayEnumerator<int>(data);
}
public Foo()
{
data = new int[500, 500];
for (int x = 0; x < 500; x++)
for (int y = 0; y < 500; y++)
{
data[x, y] = x + y;
}
}
static void Main()
{
Test(1); // for JIT
Test(500); // for real
Console.ReadKey(); // pause
}
static void Test(int count)
{
Foo foo = new Foo();
int chk;
Stopwatch watch = Stopwatch.StartNew();
chk = 0;
for (int i = 0; i < count; i++)
{
foreach (int j in foo.Basic())
{
chk += j;
}
}
watch.Stop();
Console.WriteLine("Basic: " + watch.ElapsedMilliseconds + "ms - " + chk);
watch = Stopwatch.StartNew();
chk = 0;
for (int i = 0; i < count; i++)
{
foreach (int j in foo.Bespoke())
{
chk += j;
}
}
watch.Stop();
Console.WriteLine("Bespoke: " + watch.ElapsedMilliseconds + "ms - " + chk);
}
}
答案 1 :(得分:3)
在致电IEnumerable<item>
之前将您的数组投放到GetEnumerator()
,您将获得通用IEnumerator
。例如:
string[] names = { "Jon", "Marc" };
IEnumerator<string> enumerable = ((IEnumerable<string>)names).GetEnumerator();
它可能仍然比使用foreach
(C#编译器以不同方式执行)直接枚举数组慢一点,但至少你不会有任何其他方式。
编辑:
好的,你说你的另一次尝试使用了索引器。你可以尝试这种方法,虽然我认为它不会更快:
public IEnumerable<Item> Items
{
get
{
foreach (Item x in items)
{
yield return x;
}
}
}
另一种方法是尝试避免使用二维数组开始。这是绝对的要求吗?在创建它之后,您经常迭代一个数组?可能值得在创建时轻微点击以使迭代更便宜。
编辑:另一个建议,稍微偏离墙壁...为什么不让调用者使用委托来说明如何处理每个项目?
public void ForEachItem(Action action)
{
foreach (Item item in items)
{
action(item);
}
}
缺点:
答案 2 :(得分:1)
如何在类中添加索引器:
public MyInsideArrayType this[int index]
{
get{return this.insideArray[index];
}
如果你真的需要foreach功能:
public IEnumerable<MyInsideArrayType> GetEnumerator()
{
for(int i = 0; i<this.insideArray.Count;i++)
{
yield return this[i];
}
}
答案 3 :(得分:-6)
所有形式的迭代都很便宜。如果这个时代的任何人设法以某种方式编写和发布昂贵的迭代器,他们将(正确地)在火刑柱上被烧毁。
过早优化是邪恶的。
干杯。基思。