【问题标题】:Create new instance or just set internal variables创建新实例或仅设置内部变量
【发布时间】:2026-01-15 11:00:02
【问题描述】:

我编写了一个辅助类,它在构造函数中接受一个字符串,并提供许多 Get 属性来返回字符串的各个方面。目前设置线的唯一方法是通过构造函数,一旦设置就无法更改。由于这个类只有一个内部变量(字符串),我想知道我应该保持这种方式还是应该允许设置字符串?

一些示例代码我的帮助我为什么要问:

StreamReader stream = new StreamReader("ScannedFile.dat");
ScannerLine line = null;
int responses = 0;
while (!stream.EndOfStream)
{
  line = new ScannerLine(stream.ReadLine());
  if (line.IsValid && !line.IsKey && line.HasResponses)
    responses++;
}

以上是计算给定扫描文件中有效响应数量的快速示例。像这样编码会更有利吗?

StreamReader stream = new StreamReader("ScannedFile.dat");
ScannerLine line = new ScannerLine();
int responses = 0;
while (!stream.EndOfStream)
{
  line.RawLine = stream.ReadLine();
  if (line.IsValid && !line.IsKey && line.HasResponses)
    responses++;
}

此代码用于 ASP.net Web 应用程序的后端,需要具有一定的响应性。我知道这可能是过早优化的情况,但我正在对此进行编码以提高客户端的响应能力和可维护性。

谢谢!

编辑 - 我决定也包含该类的构造函数(是的,这就是它的真正含义。):

public class ScannerLine
{
  private string line;
  public ScannerLine(string line)
  {
    this.line = line;
  }

  /// <summary>Gets the date the exam was scanned.</summary>
  public DateTime ScanDate
  {
    get
    {
      DateTime test = DateTime.MinValue;
      DateTime.TryParseExact(line.Substring(12, 6).Trim(), "MMddyy", CultureInfo.InvariantCulture, DateTimeStyles.None, out test);
      return test;
    }
  }

  /// <summary>Gets a value indicating whether to use raw scoring.</summary>
  public bool UseRaw { get { return (line.Substring(112, 1) == "R" ? true : false); } }

  /// <summary>Gets the raw points per question.</summary>
  public float RawPoints
  {
    get
    {
      float test = float.MinValue;
      float.TryParse(line.Substring(113, 4).Insert(2, "."), out test);
      return test;
    }
  }
}

**EDIT 2 - ** 我包含了该类的一些示例属性以帮助澄清。如您所见,该类从扫描仪中获取固定字符串,并且可以更轻松地将行拆分为更有用的块。该文件是来自 Scantron 机器的行分隔文件,解析它的唯一方法是一堆 string.Substring 调用和转换。

【问题讨论】:

  • 你应该将 StreamReader 包含在 using 语句中。
  • 哈哈,其实我做完后有一个显式的close,不过我清理了代码专心解决问题。

标签: c# .net asp.net optimization class


【解决方案1】:

如果你真的需要这个类,我肯定会坚持使用不可变版本。不变性使您的代码更容易推理 - 如果您存储对 ScannerLine 的引用,那么知道它不会改变是很有用的。性能几乎可以肯定是微不足道的——读取行所涉及的 IO 可能比创建新对象更重要。如果您真的关心性能,则应该在根据这些性能问题做出设计决策之前对代码进行基准测试/分析。

但是,如果您的状态只是一个字符串,那么与直接存储字符串并在以后使用适当的方法来分析它们相比,您真的提供了很多好处吗? ScannerLine 是分析字符串并缓存该分析,还是真的只是一堆解析方法?

【讨论】:

  • 在内部它包含一堆 get 属性,可以解析出各种格式的字符串并执行转换等。在这种情况下,它被编写为可重用以快速分离字符串。
  • 但是那些不能只是接受字符串参数的静态方法吗?我并不是说它必然是一个更好的主意,但它听起来更简单。如果您使用的是 C# 3.0,则可以将它们设为扩展方法。
  • 目前我正在编写 C# 2.0。我更新了这个类,以帮助澄清它的用途。
  • 我知道问题是“这两个选项中的哪一个”,但我喜欢静态方法的想法。
  • 此时我将保持原样(因为它是一个运行多个用户的 Web 应用程序)。感谢您的洞察力!
【解决方案2】:

你的第一种方法更清楚。性能方面,您可以获得一些东西,但我认为不值得。

【讨论】:

    【解决方案3】:

    我会选择第二个选项。它更有效,而且它们都同样容易理解 IMO。另外,您可能无法知道 while 循环中的这些语句将被调用多少次。那么谁知道呢?这可能是 0.01% 的性能提升,或 50% 的性能提升(不太可能,但可能)!

    【讨论】:

      【解决方案4】:

      不可变类有很多优点。像这样的简单值类是不可变的是有意义的。对于现代 VM,类的对象创建时间很短。你拥有它的方式很好。

      【讨论】:

        【解决方案5】:

        我实际上会完全抛弃类的“实例”性质,并将其用作静态类,而不是像现在这样的实例。每个属性都是完全独立的,除了使用的字符串。如果这些属性彼此相关,和/或每次分配字符串时都设置了其他“隐藏”变量(例如预处理属性),那么就有理由这样做重新分配的方式或其他方式,但是从您在那里所做的事情来看,我会将其更改为类的 100% 静态方法。

        如果您坚持让类成为实例,那么出于纯粹的性能原因,我将允许重新分配字符串,因为 CLR 不会持续创建和销毁同一类的实例(除了显然是字符串本身)。

        归根结底,IMO 这是您真正可以做的任何事情,因为没有其他类实例变量。做一个或另一个可能有风格的原因,但在解决这个问题时很难“出错”。如果在构造时设置了类中的其他变量,那么这将是一个完全不同的问题,但现在,您认为最清楚的代码。

        【讨论】:

        • 我同意除了“纯性能”部分之外的所有内容。首先读取字符串可能会花费更长的时间 - 在 .NET 中创建对象非常便宜,并且不可变性 类型的一个很好的属性。但是,是的,只封装一个字符串似乎有点没有意义。
        【解决方案6】:

        我会选择你的第一个选项。在您的示例中,类没有理由是可变的。保持简单,除非您确实需要使其可变。如果您真的很在意性能,请运行一些性能分析测试,看看有什么区别。

        【讨论】: