Posted by Pavel Podlipensky on December 11 2:03 AM
Я, как вы знаете, пишу не только статьи в свой блог, но и код. Программный код. Поэтому, меня зачастую интересуют вопросы, которые нормальным людям в голову и не лезут. Суть сегодняшней дилеммы очень простая – хорошо ли генерировать исключительные ситуации из конструктора? Это скорее философский или этический вопрос, нежели сложная техническая задача. И все же.

Конструкторы не возвращают каких-либо значений, поэтому было бы здорово сгенерировать ошибку, в случае, если объект не может быть создан. И таким образом, оповестить всех что этот объект использовать дальше нельзя. Уже слышу протестующие крики в свой адрес: “…Конструкторы предназначены для простых операций типа инициализации объекта…” или “…Лучше после попытки создания объекта проверить его на null…”.

А я не согласен с этим!

Когда мы передаем в конструктор некие параметры, мы обязаны проверить их на валидность. А как вы сообщите об “испорченном” параметре? С помощью null? Что? Опять протесты?

“…А давайте оставим конструктор пустым и добавим метод Initialize, который и вернет нам необходимый код состояния инициализации объекта…”

Хм, а конструкторы тогда зачем? Для тех, кто все еще сомневается в моих доводах, советую прочесть презентацию Bjarne Stroustrupsa (он настолько крутой, что придумал С++) об “Standard-Library Exception Safety”. И там, все вышесказанное, размазано по 36 страницам. Поэтому генерировать исключительные ситуации в конструкторе – это нормально (особенно, если вы исповедуете RAII).

В C#.NET есть еще одна приятная особенность – finalizer вызовится даже в случае ошибки в конструкторе и ресурсы будут освобождены корректно. Кстати и в самом .NET Framework многие базовые классы генерируют исключительные ситуации из конструктора, например Guid, DateTime, Queue, FileStream и другие.

А вы генерируете ошибки в конструкторах?

Posted by Pavel Podlipensky on September 26 12:18 AM

Один из моментов, который необходимо учесть при работе с Entity Framework - это как работать с таблицами без первичных ключей. По умолчанию сущность такой таблицы(proxy-класс) будет мэпится не к физической модели таблицы, а к так называемому Defining Query.Чтоб очень это мешало - так нет, но обновлять/изменять данные в этой таблице сразу после генерации класса вы не можете. Да и дизайнер не показывает, что это таблица только на посмотреть (и это не просто слова, а намек тем, кто этот дизайнер пишет). Поэтому вы получите ошибку времени выполнения Unable to update the EntitySet 'Events' because it has a DefiningQuery and no <DeleteFunction> element exists in the <ModificationFunctionMapping> element to support the current operation.

И это еще не все проблемы на которые вы можете тут наступить. Во время генерации вашего прокси-класса Entity Framework пометит все хорошие поля как часть Entity Key (откровенно говоря это все не nullable и не binary поля). Правда надо отдать должное EF - во время генерации прокси-класса из базы данных, вы можете видеть такое предупреждение: "The table/view 'DemoTable' does not have a primary key defined. The key has been inferred and the definition was created as a read-only table/view." Для починить это нужно добавить в поле вашей таблицы первичный ключ, а затем обновить модель из базы данных. После нужно будет ручками подправить ключевые поля в вашей сущности(Entity). Есть еще одна возможность обновлять данные в таблице без первичного ключа - промэпить хранимые процедуры на удаление и модификацию данных.

А вообще работы много, писать некогда, но все равно буду - хоть мало, так смачно.

Я уже писал о фичах последнего решарпера, а сейчас хочу поговорить о будующих фичах этого замечательного продукта. Разработчики обещают оптимизировать работу с памятью и увеличить скорость работы продукта. В дополнение к этим улучшениям будет реализовано следующее:

  1. Будут добавлены новые способы/методы рефакторинга кода. Большинство из них будут из inline "семейства".
  2. Планируется расширить функционал для работы с соглашениями по кодированию. "Мы не собираемся реализовывать функционал AgentSmith плагина, мы лишь хотим избавить наших пользователей от рутиной работы" - заявляют разработчики.
  3. Также будет добавлена новая фича под названием Visual Build, целью которой будет отображать процесс сборки проекта в студии. Фича была заявлена как платформа для будующей фичи по оптимизации процесса сборки.
  4. Ожидается полная поддержка Visual Basic 9 с его анонимными функциями и XML литералами.

И не забывайте, что все вышеописанное возможно благодаря вашим feedback'ам и даже идеям.

Ссылки по теме:

Прорезь в копилке с идеями (бросать только идеи на $1 млн.)

База знаний Решарпера

Официальный сайт Resharper'a 

Ребята из JetBrains выпустили новый решарпер и теперь он доступен для "покупки" и скачивания с их официального сайта.

Пожалуй самая интересная фича нового решарпера - поддержка C# 3.0 и LINQ. Но первая вещь которую я заметил, это "Reformat" переименовали в "Cleanup Code", и поначалу, это сбивало с толку. Зато сейчас эта фича поддерживает профайлы, т.е. различные профайлы могут делать различные "очистки кода": переход к авто-свойствам, использование анонимных типов, формирование readonly полей, если это возможно и многое другое.

 

Но единственное, что у меня пока не получилось - это редактирование стандартных профайлов.

Следующее, на что я обратил внимание это инициализация объектов, к примеру если я напишу

Task t = new Task(); t.Name = "Test";

То решарпер мне предложит поступить следующим образом:

Task t = new Task {Name = "Test"};

Аналогичным образом решарпер предлагает использовать implicit type variable:

Решарпер советует использовать var везде, где это возможно. Такое решение далеко не всегда оправдано, поэтому я отключил этот функционал.



Зачатки JetBrains.Annotation были еще в решарпере версии 2.5. Если помните был такой "Null Reference Analysis", который оповещал разработчика о возможных NullReferenceException в коде. Чтобы избежать такого анализа разработчики добавляли к свойствам, атрибуты NotNull или CanBeNull, которые решарпер позже использовал для инициализации состояния переменных. В новой же версии количество таких атрибутов значительно увеличилось. Скажем, если вы хотите явно указать, что ваш строковый параметр будет обрабатываться с помощью string.Format, то можете написать следующее

[StringFormatMethod("key")] public void Put(string key, params object[] args) { ... }

После чего вызвав эту функцию

Put("testing {0}, {1}, {2}", 1, 2);

решарпер подскажет, что переменная для аргумента {2} отсутствует. Но использования этой фичи требует включения нескольких JetBrain библиотек в ваш проект, что несколько смущает...

Довольно удобным стал CamelHumps Completion, позоволяющий писать лишь заглавные буквы ключевых слов, названий классов, переменных:

if(CVM.I.SV(SCV.FU 

После нажатия магического сочетания клавиш <Ctrl-Shift-Enter>, получаем

if (CodeViewManager.Instance.SupportsView(StandardCodeViews.FindUsages)) { }

Как я уже упоминал, теперь решарпер полностью поддерживает C# 3.0 и LINQ, что не может не радовать.

 

Recent Edits позволяет быстро получить доступ к недавно редактируемым участкам кода (CTRL + "-" уже просто достал!):

 


И в заключение, хочу добавить, что создатели решарпера клянутся, что он стал быстрее, особенно в обработке ASP.NET кода.

Полезные ссылки:

Официальный сайт решарпера

Новые фичи четвертого решарпера 

Скачать Resharper 4.0 

Купить Resharper 4.0 

Блог Ильи Рыженкова, продукт-менеджера компании JetBrains 

Речь пойдет о коллекциях IDictionary. Не знаю как вам, но мне надоела постоянная проверка .ContainsKey(), каждый раз, когда я хотел получить значение из словаря.

Dictionary<string, string> dict;
if (dict.ContainsKey("key"))
	value = dict["key"];
else
	value = "defaultValue";

Поэтому мне пришлось реализовать такую вот нехитрую обертку:

public static class MyExtensions
{
	public static TValue GetValue<TKey, TValue>(
	this IDictionary<TKey, TValue> source, TKey key, TValue defaultValue)
	{
		if (source.ContainsKey(key))
			return source[key];
		else
		return defaultValue;
	}
}

И теперь получить значение стало намного проще:

value = dict.GetValue("key", "defaultValue");