【问题标题】:Is there any benefit (semantic or other) to using a static method that calls a constructor?使用调用构造函数的静态方法有什么好处(语义或其他)?
【发布时间】:2014-06-20 01:46:33
【问题描述】:

我刚刚更新了 Visual Studio 2013,我注意到在 MVC 应用程序的项目模板中,ApplicationDbContext 类现在有一个只调用构造函数的静态方法:

public static ApplicationDbContext Create()
{
    return new ApplicationDbContext();
}

这对我来说似乎很混乱,但我想有一些语义原因我现在应该开始使用 ApplicationDbContext.Create() 而不是 new ApplicationDbContext()。这样做有什么好处吗?

【问题讨论】:

  • 这似乎是一个糟糕的工厂实现。
  • 构造函数是私有的吗? ...这是一种模式,用于阻止消费者只创建一个对象而不指定它的要求 - 强制您通过静态方法。当然,这个似乎没用 - 但如果你想扩展它,设置就在那里。
  • 构造函数不是私有的。
  • 如果您使用 Autodesks API,它们都实现了这种模式 - 但它们将构造函数设为私有。例如; static Pipe Document::CreateNewPipe(...);static Curve Document::CreateNewCurve(...); 老实说,我觉得它非常烦人而且没用。然而,它可能对 API 设计人员有用,以确保对象内聚。
  • 谷歌“四人帮”一书并阅读其中描述的设计模式的概述。这是工厂模式的一个简单实现,书中描述了其他 22 种设计模式。熟悉它们至少会有助于识别它们并知道何时使用它们。

标签: c# constructor static-methods


【解决方案1】:

其实。是的。

在您的特定情况下,包装它可以让您快速开始使用逻辑,例如创建 ApplicationDbContext 和单例或以通用方式为整个应用程序处理异常。由于构造函数不能返回 null,因此能够捕获异常并返回 null 非常重要。

Tuple.Create 是泛型推理的主要示例,它不适用于构造函数。这让你说

Tuple.Create(Item1, Item2.. ItemN);

并且让编译器推断类型,而不是

new Tuple<T1, T2...Tn>(Item1, Item2...ItemN);

如果您想切换其中一种类型,则更冗长,并且需要更多的工作。

还有匿名类型的情况,它不能明确指定,因此不能在新语句中使用。我特别有过这样的情况,在搜索程序集以查找特定属性以链接命令结构时,我想在搜索期间从匿名类型中创建一个可枚举(在本例中为队列),以将类引用与其构造函数配对和字符串参数,而不是每次需要时都查找它们。由于我可以再次在方法中使用通用推理,我能够将构造函数包装在扩展方法中并完成工作。

也存在单例模式的情况,您希望“GetInstance”方法通常创建一个值,或者如果存在则获取一个值。可能不符合条件,因为它只是包装了一个构造函数。

此外,在很多情况下,您可能想要控制实现过程,例如将它们强制到其他线程上,将它们记录到数据库中以便稍后撤消,或者连接到权限系统,所有这些都可以通过制作一个构造函数包装器并添加几行逻辑,然后将构造函数私有化以避免直接调用它来完成。

在某些情况下,我创建了一个委托给已知子代的工厂方法,以便根据提供的参数提供返回接口或抽象的不同实现。这具有能够隐藏实现类的额外好处——Type 类和 IEnumerable 接口利用了这种模式。

【讨论】:

  • 旁注:构造函数类型参数推断(允许new Tuple(1, 2))是 C# 6 中预期的新功能之一。
  • 这是一个很好的答案。我想知道但我没有明确说明的一件事是,您是否有任何理由使用现在的静态方法。如果没有,我想它只是在你需要它的情况下才存在,对于每个版本都会变得更加混乱的项目模板来说,这似乎是不必要的(尽管鼓励使用有益的模式)。
【解决方案2】:

这种模式非常有用,特别是如果您使用私有构造函数,并从 Create 返回一个接口类型,而不是具体类型。

private ApplicationDbContext()
{

}

public static IApplicationDbContext Create()
{
    return new ApplicationDbContext();
}

现在,您的类的消费者不再依赖具体的实现——他们只能依赖抽象。

【讨论】:

【解决方案3】:

使用静态方法(创建方法)包装构造函数允许您选择传达信息的特定名称。您还可以创建多个具有相同参数签名的方法,例如 CreateX(float f)CreateY(float f),这是构造函数无法做到的。

这确实有用的情况是,例如用于创建表示可能具有多个单位(例如时间、长度或重量)的物理量的结构。在这里,您可以使用创建方法来强制程序员始终显式指定单位,而不是仅将无单位的数字传递给单个构造函数(假定某个单位,如果出错可能有huge consequences)。

例子:

public struct Length
{
  private const double MetersPerYard = 0.9144;
  private double _meters;

  private Length(double meters)
  {
    _meters = meters;
  }

  public static Length FromMeters(double meters)
  {
    return new Length(meters);
  }

  public static Length FromYards(double yards)
  {
    return new Length(yards*MetersPerYard);
  }

  public double Meters 
  {
    get { return _meters; } 
  }

  public double Yards
  {
    get { return _meters / MetersPerYard; }
  }
}

或查看TimeSpanFromMinutesFromSeconds 等方法。

【讨论】:

    猜你喜欢
    • 2011-01-27
    • 1970-01-01
    • 2011-05-29
    • 1970-01-01
    • 2011-12-08
    • 1970-01-01
    • 1970-01-01
    • 2012-08-30
    相关资源
    最近更新 更多