一般的OOP,特别是c#——构造函数应该快速返回吗?

本文关键字:返回 构造函数 OOP 特别是 | 更新日期: 2023-09-27 18:13:38

我目前正在为我的工作重构一些旧代码。一些白痴(我,2年前)写了一些我认为很臭的东西。我有这样一种感觉(我可能在某个地方读过,忘记了源代码),c#中的构造函数应该快速返回,因为一些技术细节,可能与垃圾收集有关。即以下

class A
{
     public object Result {get; private set;}
     private object RunLongOperation(){/* ... */}
     public A(){
         Result = RunLongOperation();
     }
}

是不好的做法。所以我的问题是双重的- 它实际上是坏的,如果是,为什么?可以重写为

class A
{
     public object Result {get; private set;}
     private static object RunLongOperation(){/* ... */}
     private A() { }
     public static A Make(){
        return  new A { Result = RunLongOperation() };
     }
}

通过一种工厂静态方法。对我来说,这似乎是多余的代码,但实际的对象是快速构建的。

要发光,构造函数接受一些参数并在RunLonOperation()中渲染图像,并根据输入参数做一些其他事情。然后将类简化为不可变的结果容器。根据参数不同,耗时大约10 ~ 20秒。

一般的OOP,特别是c#——构造函数应该快速返回吗?

是的,从可测试性的角度来看,在构造函数中做实际工作是一件坏事。

为一个在构造函数中做大量工作的类编写测试是非常困难的,因为你没有任何方法可以改变该对象所需的依赖关系,或者通过模拟该对象注入一些自定义行为。

请参阅http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/了解有关此设计缺陷的详细解释。

我不认为应该有一个通用的规则,但实际上最好使用工厂模式,不要在构造函数中处理太多的事情。

我不认为有技术要求(例如GC)构造函数不应该超过一定的时间。然而,从程序员的角度来看,我当然不认为new -ing一个对象会花费很长时间。对我来说,工厂方法似乎更适合这个用途。

您还可以考虑删除长时间运行的操作,并将结果注入构造函数并使用RenderImageFactory代替。将使过程更明显,可能有助于单元测试(意思是:如果你想单元测试类A,你可能不希望每次都渲染图像,而是能够模拟它,以加快速度,减少测试设置开销)

没有硬性规定构造函数可能需要多长时间。它取决于根据对象的功能,你可以合理地期望得到什么。

如果可以使用延迟加载,您应该考虑这对您的类是有好处的。例如,如果你在构造函数中加载不同的东西,并且其中一些并不总是被使用,那么在真正需要它们的时候加载它们可能会更好。

除此之外,如果需要的话,通常没有很好的理由将工作放在构造函数以外的任何地方。

构造函数完全没有理由不花足够长的时间来完成它的工作。在我看来,您和其他人提出的选项将使您的代码变得更加复杂,而没有明显的好处。

Be lazy:

class A 
{
     private A() { } 
     private static object _Result;
     public static object Result 
     {
         get
         {
              if (_Result == null)
                   _Result = RunLongOperation();
              return _Result;
         }
     }
     private static object RunLongOperation(){/* ... */} 
} 

任何时候我们认为来自. tor的长时间任务调用都是一个糟糕的设计气味的好迹象。我一直在我的脑海里,直到我卡住了一个类似的问题,运行日志代码类型。在我的情况下,我将在构造函数中异步运行Task<T>并解决问题,我根本无法摆脱任务,因为它完全是关于反射的,您的手远离任何良好的设计原则实现。

Thread.SuspendThread.Pause似乎是从构造函数运行长任务的唯一危险候选,但它们已经过时了(在1.1之后),现在似乎没有任何理由限制.ctor的执行,但任何API设计的直观原则(.ctor只是一种快速"准备"对象以供进一步使用的方法)。MSDN对此有很好的指导)。

我的建议是使用Factory,因为构造函数通常不是放置业务逻辑的地方。如果你可以重构,那么你应该。如果你不能没有人会因此惩罚你的

如果长时间运行的操作是引用透明的,那么您只能对'Result'的每个可能结果运行一次,并为每个对象实例重用这些结果。这可能会带来性能上的好处。如果有多个可能的结果,那么你需要一些方法来查找哪个结果是合适的。

这只是一个粗略的例子,可能需要一些调整:

class A 
{      
   static Dictionary<String, Object> memoizationCache;
   public static bool TryGetMemoizedResult(A instance, out object result)
   {
       // do the checking etc. 
       result = memoizationCache[instance.SomeMember]; 
   } 
   public static void AddMemoizedResult(A instance, object result)
   {
       memoizationCache.Add(instance.SomeMember, result); 
   } 
   public object Result {get; private set;}   
   public string SomeMember { get; private set; }   
   private object RunLongOperation(){/* ... */}   
   public A()
   {   
       object result;
       if (TryGetMemoizedResult(this, out result))
       {
            Result = result;
       }
       else 
       {
            Result = RunLongOperation();  
            AddMemoizedResult(this, this.Result); 
       }    
   } 
} 

我提出这个问题的原因是,在您的情况下,操作可能是引用透明的,但如果不是,那么我将使用延迟加载方法。就目前而言,我认为两种初始化技术之间不会有很大的性能差异。