在循环中,我应该重用单个类实例,还是创建新实例?
本文关键字:实例 新实例 创建 我应该 循环 单个类 | 更新日期: 2023-09-27 18:06:03
例如
Aclass myAclass = new Aclass();
for(int i=0;i<n;i++)
{
myAclass.load(i);
myAclass.do();
myAclass.clear() // clear the state of myAclass, rdy for next iteration.
}
或者如果我将' myclass .load()嵌入到类构造函数中,并做如下操作:
for(int i=0;i<n;i++)
{
Aclass myAclass = new Aclass(i);
myAclass.do();
}
那么哪种方式是更好的实践呢?顺便说一句,标题似乎不适合内容,帮我找一个更合适的标题。
注意:类的构造函数是平凡的,Load()不是,Clear()是平凡的(当然,第二个例子中的构造函数并不简单,因为它触发了Load())
我打算逆潮流而行,建议采用第二种方法(假设构造简单)。垃圾收集器在短生命周期的对象上茁壮成长,第二种方法
- 更容易理解,因为您不会通过重用相同的对象在循环迭代之间共享状态
- 由于对象的独立性是可并行化的(假设没有其他隐藏的共享状态)
一般来说,在c#中,垃圾收集器会为你处理所有这些。
如果你想在处理完一个对象后明确地处置它,确保它实现了IDisposable,并在循环中执行以下操作:
for (int i = 0; i < infinity; i ++)
{
using (MyClass myObject = new MyClass(i))
{
myObject.DoWork();
...
} // The object will dispose here
}
没有办法判断哪种方法在性能方面是最好的,这取决于类的实现。
现在,作为一般规则,您应该避免可变对象,因此重用类的相同实例来做其他事情通常不是一个好主意。所以我倾向于第二种方法。当然,如果类的初始化是一个昂贵的操作,那么第一种方法将更快……
至于内存使用,第二种方法将在内存中创建更多实例,但它们最终将由GC收集,因此不应该是一个问题。唯一的缺点是它给GC带来了更大的压力,所以如果有很多迭代,它可能会对性能产生负面影响。
对于性能而言,它实际上取决于构建Aclass
类实例与调用Clear()
的成本有多高,您可以通过运行10000次迭代的循环来测试哪个行为更好,并使用System.Diagnostics.Stopwatch
来测试哪个性能更好。所以:
int n = 100000;
Stopwatch watch = Stopwatch.StartNew();
Aclass myAclass = new Aclass();
for(int i=0; i<n; i++)
{
myAclass.load(i);
myAclass.do();
myAclass.clear() // clear the state of myAclass, rdy for next iteration.
}
watch.Stop();
Console.WriteLine(watch.ElampsedMilliseconds);
watch.Reset();
watch.Start();
for(int i=0;i<n;i++)
{
Aclass myAclass = new Aclass(i);
myAclass.do();
}
watch.Stop();
Console.WriteLine(watch.ElampsedMilliseconds);
编辑:作为@Mark注释的一对,我们应该首先调用没有时间测量的函数,然后运行时间测试。所以:
AClass foo = new AClass();
foo.Load(0);
foo.do();
foo.clear();
然后测试代码张贴在上面"由int n = 100000;
开始"。
很大程度上取决于Aclass的结构。
第一个选项通常会使用更少的内存,因为您正在重用类实例。我说通常是因为如果Aclass的状态包含很多引用类型(字符串,其他类),那么"清除"它(将这些引用设置为null)仍然会将这些对象留在堆上供垃圾收集器清理。由于清除对象所花费的时间,它也可能更慢(并且可能更容易出错)。
第二种方法通常会更快(.net中的分配非常非常快),但通常会使用更多内存。这是。net、java和其他垃圾收集平台中的常见模式,因为垃圾收集器的目的是模拟具有无限内存的环境(参见Raymond Chen的文章)。
就性能而言,第一个选项是最好的方法。在第二个for循环中,每次迭代循环时,将实例化一个新的Aclass实例并在堆上分配。您将在堆上留下这个类的许多实例,CLR将不得不对这些实例进行垃圾收集。垃圾收集减慢了正在运行的程序的执行速度,因为必须占用宝贵的CPU周期来释放内存。
想想看,如果你的循环迭代1000次,它会在堆上留下1000个对象。即使每个对象都是5mb甚至2MB,这也会大大降低程序的速度。选第一个。作为旁注,我更喜欢load方法而不是构造函数,因为我发现它更容易理解您对该类所做的操作。