Как создать неизменяемые объекты в C #? - PullRequest
31 голосов
/ 24 февраля 2011

В вопросе о Лучшие практики для проверки шаблонов C # , ответ с наибольшим количеством голосов говорит:

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

Как именно вы создаете неизменный объект в C #? Вы просто используете ключевое слово readonly? 1010 *

Как именно это будет работать, если вы хотите проверить в конструкторе класса модели, сгенерированного вашей Entity Framework?

Будет ли это выглядеть ниже?

public partial readonly Person
{
    public Person()
}

Ответы [ 6 ]

67 голосов
/ 24 февраля 2011

Интересным вопросом здесь является ваш вопрос из комментариев:

Какой тип объекта у вас есть, чтобы вам не нужно было изменять значения в какой-то момент? Я предполагаю, не класс модели, правильно? Мне пришлось изменить имя человека в моей базе данных - это не соответствует этой идее.

Хорошо, рассмотрим вещи, которые уже неизменны. Числа неизменны. Как только у вас есть номер 12, это 12. Вы не можете изменить его. Если у вас есть переменная, которая содержит 12, вы можете изменить содержимое переменной на 13, но вы изменяете переменную , а не число 12 .

То же самое со строками. «abc» - это «abc», и он никогда не меняется. Если у вас есть переменная, которая содержит «abc», вы можете изменить ее на «abcd», но это не изменит «abc», это изменит переменную.

А как насчет списка? {12, «abc»} - это список, за которым следует 12, за которым следует «abc», и этот список никогда не меняется. Список {12, "abcd"} представляет собой другой список .

И вот тут-то и рельсы. Потому что в C # вы можете сделать это в любом случае. Вы можете сказать, что между этими двумя списками есть ссылочный идентификатор , если спискам разрешено изменять свое содержимое без изменения их идентификатора.

Вы ударяете гвоздь прямо по голове, когда говорите о «модели». Вы моделируете что-то, что меняется? Если это так, то, возможно, целесообразно смоделировать его с изменяющимся типом. Преимущество этого заключается в том, что характеристики модели соответствуют моделируемой системе. Недостатком является то, что становится очень сложно сделать что-то вроде «отката», когда вы «отменяете» изменения.

То есть, если вы изменили {12, "abc"} на {12, "abcd"}, а затем хотите откатить мутацию, как вы это сделаете? Если список неизменен, вы просто сохраняете оба значения и выбираете, какое из них вы хотите использовать в качестве «текущего» значения. Если список является изменяемым, то вы должны иметь логику отмены, сохраняющую «функцию отмены», которая знает, как отменить мутацию.

Что касается вашего конкретного примера, вы, безусловно, можете создать неизменяемую базу данных. Как вы меняете имя человека в вашей неизменной базе данных? Вы не Вы создаете новую базу данных, в которой есть нужные вам данные. Хитрость в неизменяемых типах заключается в том, чтобы делать это эффективно, не копируя миллиарды байтов. Конструкция неизменяемой структуры данных требует поиска умных способов разделения состояния между двумя почти идентичными структурами.

11 голосов
/ 24 февраля 2011

Объявление всех полей только для чтения - хороший шаг к созданию неизменяемого объекта, но одного этого недостаточно.Это связано с тем, что поле только для чтения все еще может быть ссылкой на изменяемый объект.

В C # неизменность не обеспечивается компилятором.Вы просто должны быть осторожны.

6 голосов
/ 24 февраля 2011

Этот вопрос имеет два аспекта:

  1. Неизменяемый тип при создании экземпляра объекта
  2. Неизменяемый тип при создании экземпляра объекта EF

Первый аспект требует структурированиявот так:

public class MyClass
{
  private readonly string _myString;
  public string MyString
  {
    get
    {
      return _myString;
    }
  }

  public MyClass(string myString)
  {
    // do some validation here

    _myString = myString;
  }
}

Теперь проблема - ЭФ.EF требует конструктора без параметров, а EF должен иметь установщики свойств.Я задал очень похожий вопрос здесь .

Ваш тип должен выглядеть следующим образом:

public class MyClass
{
  private string _myString;
  public string MyString
  {
    get
    {
      return _myString;
    }
    private set
    {
      _myString = value;
    }
  }

  public MyClass(string myString)
  {
    // do some validation here

    _myString = myString;
  }

  // Not sure if you can change accessibility of constructor - I can try it later
  public MyClass()
  {}
}

Вы также должны сообщить EF о частном установщике свойства MyString - это настраивается вСвойства объекта в файле EDMX.Очевидно, что не будет валидации, когда EF материализует объекты из БД.Также вы не сможете использовать такие методы, как ObjectContext.CreateObject (вы не сможете заполнить объект).

Шаблон объекта Entity T4 и генерация кода по умолчанию создают метод фабрики CreateMyClass вместо конструктора с параметрами.Шаблон POCO T4 не генерирует фабричный метод.

Сначала я не пробовал это с кодом EF.

6 голосов
/ 24 февраля 2011
2 голосов
/ 24 февраля 2011

Объект неизменного значения - это объект значения, который нельзя изменить.Вы не можете изменить его состояние, вам нужно создать новые

Посетите блог Эрика Липперта:

Виды неизменности http://blogs.msdn.com/b/ericlippert/archive/2007/11/13/immutability-in-c-part-one-kinds-of-immutability.aspx

Посмотрите на

Шаблон неизменяемого объекта в C # - как вы думаете?

1 голос
/ 24 февраля 2011

Как именно это будет работать, если вы хотите проверить в конструкторе класса сгенерированной модели Entity Framework?

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

Но вы можете использовать неизменяемые объекты далее в своем коде.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...