在循环中,我应该重用单个类实例,还是创建新实例?

本文关键字:实例 新实例 创建 我应该 循环 单个类 | 更新日期: 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())

在循环中,我应该重用单个类实例,还是创建新实例?

我打算逆潮流而行,建议采用第二种方法(假设构造简单)。垃圾收集器在短生命周期的对象上茁壮成长,第二种方法

  1. 更容易理解,因为您不会通过重用相同的对象在循环迭代之间共享状态
  2. 由于对象的独立性是可并行化的(假设没有其他隐藏的共享状态)

一般来说,在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方法而不是构造函数,因为我发现它更容易理解您对该类所做的操作。