C#构造函数设计

我有一个你在一个文件夹中传递的类,然后它会关闭并处理指定文件夹中的大量数据。

例如:

MyClass myClass = new MyClass(@"C:\temp"); 

现在它关闭并读取说几千个文件并用数据填充该类。

我应该从构造函数中移出这些数据并将其作为单独的方法,例如以下方法吗?

 MyClass myClass = new MyClass(); myClass.LoadFromDirectory(@"C:\temp"); 

也许你应该用这种方式尝试使用返回对象实例的静态方法。

 var myClass = MyClass.LoadFromDirectory(@"C:\temp"); 

这将使初始化代码保持在构造函数之外,并为您提供您正在寻找的“一行”声明。


从海报中继续下面的评论,通过添加State实现可能是这样的:

 public class MyClass { #region Constructors public MyClass(string directory) { this.Directory = directory; } #endregion #region Properties public MyClassState State {get;private set;} private string _directory; public string Directory { get { return _directory;} private set { _directory = value; if (string.IsNullOrEmpty(value)) this.State = MyClassState.Unknown; else this.State = MyClassState.Initialized; } } #endregion public void LoadFromDirectory() { if (this.State != MyClassState.Initialized || this.State != MyClassState.Loaded) throw new InvalidStateException(); // Do loading this.State = MyClassState.Loaded; } } public class InvalidStateException : Exception {} public enum MyClassState { Unknown, Initialized, Loaded } 

这取决于。 您应该评估class级的基本目的。 它的function是什么?

我通常喜欢的是让类构造函数执行类的运行所需的初始化。 然后我调用类上的方法,可以安全地假设已经完成了必要的初始化。

通常,初始化阶段不应过于密集。 执行上述操作的另一种方法可能是:

 // Instantiate the class and get ready to load data from files. MyClass myClass = new MyClass(@"C:\temp"); // Parse the file collection and load necessary data. myClass.PopulateData(); 

这是你的全class吗? 如果是这样,那么我会说它并不重要。 但是,你上课的可能性实际上比你所展示的更多。 例如,它有任何error handling吗?

构造函数的目的是构造一个对象。 方法的目的是执行操作。 所以我的投票是这种forms:

 MyClass myClass = new MyClass(); myClass.LoadFromDirectory(@"C:\temp"); 

我同意Ari和其他人 – 将他们分开。

构造函数应该真正完成最少量的工作(只需初始化准备好使用的对象并保留它)。 通过使用单独的方法来完成工作:

  • 呼叫者更清楚的是,工作者function可能需要很长时间。
  • 很容易提供几个构造函数来初始化具有不同信息的对象(例如,您可能能够传入可以提供路径名的自己的类(而不是字符串)。或者您可以传入一个指定通配符的额外参数要匹配的文件名,或用于指定搜索是否应该递归到子文件夹的标志)。
  • 您可以避免构造函数的任何问题。 在构造函数中,对象没有完全形成,因此执行工作可能很危险 – 例如,在构造函数中调用虚函数是一个非常糟糕的主意。 你在构造函数中输入的代码越少,你就越不可能意外地做出“坏事”。
  • 将不同的行为/function分成不同的方法是更清晰的编码风格。 保持初始化并分开工作
  • 拆分类将来更容易维护和重构。

我认为你应该根据你是否计划重用相同的对象在不同的​​输入上执行相同的操作来决定上述两种方法(“首先初始化,然后执行”vs“空初始化,用params执行”)。
如果该类仅用于在固定的参数上运行任务,我将在构造函数中初始化它(使其只读取),然后在不同的方法上执行任务。
如果你想继续在不同的参数上执行任务,我会把它放在任务方法本身。

如果所有类都执行此任务,我还会考虑将其全部更改为静态类/方法 – 它不需要保持其内部状态。

无论如何,我永远不会把任务本身放在构造函数中。 正如Cerebrus所说,初始化应该很快。

除非您的类的主要目的是执行I / O,否则您可能不应该在构造函数中执行I / O(可能抛出IOException)。

考虑将该类拆分为两个:

 interface IMyDataSource { // ... } class FileDataSource: IMyDataSource { public FileDataSource(string path) { // ... } } class MyClass { public MyClass(IMyDataSource source) { // ... } } IMyDataSource myDS = new FileDataSource(@"C:\temp"); MyClass myClass = new MyClass(myDS); 

这样,主类可以专注于管理自己的状态,而数据源则从文件内容构建初始状态。

如果这是该类使用的唯一资源,那么将路径传递给构造函数可能会更好。 否则,它将成为您的class级成员的参数。

我个人的偏好是使用C#3.0初始化程序。

 class MyClass { public string directory; public void Load() { // Load files from the current directory } } MyClass myClass = new MyClass{ directory = @"C:\temp" }; myClass.Load(); 

这有一些优点:

  • 对象实例化不会产生自动文件系统副作用。
  • 所有参数都被命名。
  • 所有参数都是可选的(但是,如果没有定义,当然可以在Load()中引发exception)
  • 您可以在实例化调用中初始化任意数量的属性,而不必重载构造函数。 例如,是否递归目录的选项,或者是要搜索的filespec的通配符。
  • 您仍然可以在目录的setter中使用某些逻辑来执行某些操作,但同样,副作用通常不是一件好事。
  • 通过在单独的过程调用中执行文件操作,可以避免在exception处理程序中无法引用myClass实例的问题。

我将在这里回应“分裂他们”的人。 如果有帮助,试试这个:

  1. 问问自己,“这个方法/属性/字段什么?”
  2. 做到这一点; 不多也不少。

在这里应用,你得到这个:

  1. 构造函数应该创建对象。
  2. 您的方法应该从文件系统加载其数据。

在我看来,这比“构造函数应该创建对象从文件系统加载其数据”更合乎逻辑。