一般的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秒。
是的,从可测试性的角度来看,在构造函数中做实际工作是一件坏事。
为一个在构造函数中做大量工作的类编写测试是非常困难的,因为你没有任何方法可以改变该对象所需的依赖关系,或者通过模拟该对象注入一些自定义行为。
请参阅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.Suspend
和Thread.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);
}
}
}
我提出这个问题的原因是,在您的情况下,操作可能是引用透明的,但如果不是,那么我将使用延迟加载方法。就目前而言,我认为两种初始化技术之间不会有很大的性能差异。