Есть ли какое-либо преимущество (семантическое или другое) для использования статического метода, который вызывает конструктор?

Я только что обновил Visual Studio 2013, и я заметил, что в шаблоне проекта для приложения MVC класс ApplicationDbContext теперь имеет статический метод, который просто вызывает конструктор:

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

Мне кажется, что это беспорядок, но я полагаю, что есть семантическая причина, по которой я должен начать использовать ApplicationDbContext.Create() вместо new ApplicationDbContext(). Есть ли какие-либо выгоды для этого?

Ответ 1

На самом деле. да.

В вашем конкретном случае обертывание таким образом позволяет быстро начать болтовку по логике, например, сделать ApplicationDbContext и singleton или обработать исключение в общем виде для всего приложения. Поскольку конструктор не может вернуть значение null, это может быть очень важно, чтобы иметь возможность поймать исключение и вернуть значение null.

Tuple.Create является ярким примером общего вывода, который не работает с конструкторами. Это позволяет вам сказать

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

И пусть компилятор выводит типы, а не

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

Что более подробно, и требуется немного больше работы, если вы хотите отключить один из этих типов.

Существует также случай Анонимных типов, который не может быть указан явно и, следовательно, не может использоваться в новых операторах. Я специально имел случай, когда при поиске сборок для определенного атрибута для связывания структуры команд я хотел сделать перечислимым (очередь в этом случае) из анонимного типа во время поиска для сопряжения ссылок класса со своим конструктором и строковые аргументы, а не искать их каждый раз, когда они нужны. Поскольку я могу снова использовать общий вывод в методе, мне удалось обернуть конструктор в метод расширения и выполнить задание.

Есть также случаи для одноэлементных паттернов, в которых вы хотите, чтобы метод GetInstance обычно создавал значение или получал его, если он существует. Может не квалифицироваться, так как он немного больше, чем завершает конструктор.

Кроме того, существует множество случаев, когда вы можете контролировать процедуры внедрения, например, принуждать их к другим потокам, регистрировать их в базе данных, которая будет отменена позже, или закрепить систему разрешений, все из которых могут быть сделанный путем создания обертки конструктора и добавления еще нескольких строк логики, а затем приватизации конструктора, чтобы избежать прямого его вызова.

Существуют также случаи, когда я создал метод factory, который делегирует известным детям, чтобы обеспечить другую реализацию возвращенного интерфейса или абстрактного на основе предоставленных параметров. Это имеет дополнительное преимущество, заключающееся в возможности скрыть классы-реализации. В этом классе используется класс Type и IEnumerable.

Ответ 2

Этот шаблон может быть очень полезен, особенно если вы используете частный конструктор и возвращаете тип интерфейса из Create, а не конкретного типа.

private ApplicationDbContext()
{

}

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

Теперь потребители вашего класса не могут зависеть от конкретной реализации - они могут полагаться только на абстракцию.

Ответ 3

Обтекание конструктора статическими методами (методами создания) позволяет вам выбрать конкретное имя, которое передает информацию. Вы также можете создать несколько методов с такой же сигнатурой параметра, как CreateX(float f) и CreateY(float f), которые вы не можете сделать с конструкторами.

Ситуация, когда это действительно полезно, является, например, для создания структур, которые представляют физические величины, которые могут иметь несколько единиц, таких как время, длина или вес. Здесь вы можете использовать методы создания, чтобы заставить программиста всегда явно указывать единицу вместо того, чтобы просто передавать единичное число в один конструктор (который предполагает определенную единицу, и, получив это неправильно, может иметь огромные последствия).

Пример:

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; }
  }
}

Или посмотрите TimeSpan и методы, такие как FromMinutes, FromSeconds и т.д.