在.Net中创建对象是否昂贵?

时间:2010-01-20 21:14:50

标签: .net class-design code-design

我刚刚重构了一个同事的代码,粗略地看起来像这样......

public class Utility
  public void AddHistoryEntry(int userID, HistoryType Historytype, int companyID)
  {
    // Do something...
  }
  public void AddHistoryEntry(int userID, HistoryType historyType, int companyID, string notes)
  {
    // Do something...
  }
}

对此...

public class HistoryEntry
{
  public long UserID { get; private set; }
  public HistoryType HistoryType { get; private set; }
  public long CompanyID { get; set; }
  public string Notes { get; set; }

  public HistoryEntry(long userID, HistoryType historyType)
  {
    this.UserID = userID;
    this.HistoryType = historyType;
  }
}

public class Utility
{
  public void AddHistoryEntry(HistoryEntry entry)
  {
    // Do something...
  }
}

}

现在,这是更好的代码设计,并且是Bob叔叔的最爱。但是,我的同事辩称,每次我们想要调用这种方法时,新建一个对象要贵得多。

他是对的吗?

进一步说明

感谢目前为止的所有回复。为了简洁起见,我遗漏了许多细节,但其中一些细节已经引起了答案的问题。

  • 实用程序类不存在。我只是想把这些方法放在一个类中供大家看。实际上,这是一个明智的课程。
  • 事实上,原始代码中有5个AddHistoryEntry方法。所有这些都需要很多int参数。重构的一个原因是AddHistory(0, 1, 45, 3);并没有真正告诉你太多!
  • AddHistoryEntry不是从紧密循环中调用的,但它在整个应用程序中被广泛使用。

更正

我现在更新了代码示例,因为我在某些参数上犯了错误。

9 个答案:

答案 0 :(得分:20)

如果你同时在内存中有数百万个这些对象,他可能是正确的。但如果你不这样做,那么他提出的几乎肯定是一个有争议的问题。始终首先选择更好的设计,然后仅在您未达到性能要求时才进行修改。

答案 1 :(得分:15)

创建一个新对象非常便宜。你必须在一个非常紧密的循环中创建很多对象以注意到任何差异......但与此同时,它不是 free 。您还需要记住,成本是隐藏的一半 - 您在创建对象时预先花费一些成本,但这也意味着垃圾收集器还有更多工作要做。

更清洁的设计是否值得追求性能?我们无法告诉您 - 这取决于您的代码的使用方式。我强烈怀疑性能损失是微不足道的,但如果你 在紧密的循环中使用它,它可能不是。现在只有一种方法可以找到:如果你担心,可以测量一下。就个人而言,我可能只是去寻求更清洁的设计,只有当它看起来成为一个问题时才检查性能。

(顺便说一句,如果这种方法击中磁盘或数据库,差异几乎绑定是无关紧要的。)

顺便说一下,有一个建议:你的HistoryEntry类型实际上是否需要是可变的?你能将所有属性设为只读,由私有只读变量支持吗?

提到威尔的观点 - 我有点赞同他。如果这两个方法只是 的地方你需要这个概念,我完全不确定是否值得为它创建一个新类型。不是因为性能方面,而是因为你引入了额外的一堆代码而没有明显的好处。这里的关键词被证明 - 如果你实际上在其他地方使用相同的参数集合,或者可以有用地将逻辑放入HistoryEntry类,那完全不同。

编辑:只是回应关于多个整数参数的观点--C#4将允许命名参数,这应该使这更容易。所以你可以打电话:

AddHistoryEntry(userId: 1, companyId: 10);

使用附加类使名称可见需要以下之一:

  • 来自C#4的命名构造函数参数(在这种情况下,你不比使用该方法更好)
  • 可变类型(urgh - 可能导致难以追踪原始版本中不存在的错误)
  • 用于构造实例的长静态方法名称(“FromUserAndCompanyId”),这些名称变得难以处理且参数更多
  • 可变构建器类型
  • 一堆“With”方法:new HistoryEntry().WithCompanyId(...).WithUserId(...) - 这使得在编译时更难以验证您提供了所有必需的值。

答案 2 :(得分:5)

为什么不在类本身中使用静态方法?

public static void AddHistoryEntry(HistoryEntry entry)

保存实用程序类。新对象可能很昂贵,但可能不是 - 但它可能并不重要。良好的默认值允许您更轻松地扩展代码,并且您将比在性能方面节省更多的维护时间,除非您在循环中执行此操作。

答案 3 :(得分:4)

你错了,但他比你错了。

答案 4 :(得分:4)

相对于而不是在.NET中创建对象而言,这是相当昂贵的,就像1是相对于0的大数字一样。这完全取决于您的观点。如果这是一个循环运行2000万次,我可能会担心;否则,我不在乎。

话虽如此,我想指出Utility对于一个类来说不是一个好名字,重构成HistoryEntry也可能需要你验证传入的参数不是null,这是当所有参数都是值类型时您没有的要求。

它通常被认为是反模式,使对象具有数据但没有行为,HistoryEntry似乎是。尽量避免经常这样做。

答案 5 :(得分:2)

在.NET中创建对象并不是很昂贵。实际上,即使您创建了数百万个对象,对象创建也很快

但是,垃圾收集便宜。如果你创建了许多短期对象,当gc启动时你可能会遇到相当大的减速。如果创建运行数千或数百万次,则避免创建新的短期对象是有意义的。例如,如果您使用具有数千个连接的服务器接收的每个网络数据包创建多个对象。

也就是说,避免在之前调用new 来证明很多对象确实减慢了gc是不明智的。在你的例子中,他们很可能不会。

答案 6 :(得分:1)

这真的是性能瓶颈吗?如果没有,那么在这种情况下避免使用对象将是过早优化的明显情况。在您开始遇到重大性能问题之前,可维护性和设计应优先于性能。

答案 7 :(得分:0)

计算成本略高,但是为了这个成本,您可以获得更清晰,更具概念性的代码,可以更快地响应未来的业务需求。性能与敏捷性。

答案 8 :(得分:0)

没有。垃圾收集器经过优化,在处理短期对象时非常高效。