<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-8596733192274108952</id><updated>2012-03-02T21:05:14.915+02:00</updated><category term='LINQ'/><category term='Windows Forms'/><category term='Visual Studio'/><category term='C# 5.0'/><category term='ZJTZ'/><category term='Управление проектами'/><category term='Юмор'/><category term='Книги'/><category term='MVP'/><category term='F#'/><category term='Тестирование'/><category term='Байки'/><category term='Google'/><category term='ООП'/><category term='Exception handling'/><category term='C++'/><category term='C#'/><category term='Обработка исключений'/><category term='.NET 4.5'/><category term='Code Review'/><category term='WCF'/><category term='Переводы'/><category term='Цитаты'/><category term='EC#'/><category term='Принципы разработки'/><category term='TeX'/><category term='Отладка приложений'/><category term='Reactive Extensions'/><category term='Философия программирования'/><category term='Анонсы'/><category term='Multithreading'/><category term='Мысли'/><category term='Short circuit'/><category term='RSDN Magazine'/><category term='WPF'/><category term='PostgreSql'/><category term='Design by Contract'/><category term='.NET'/><category term='Шаблоны проектирования'/><title type='text'>Programming stuff</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://sergeyteplyakov.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://sergeyteplyakov.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default?start-index=101&amp;max-results=100'/><author><name>Sergey Teplyakov</name><uri>https://profiles.google.com/108967431947412296254</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-2weHUILo1ns/AAAAAAAAAAI/AAAAAAAABRo/POab-anD6xA/s512-c/photo.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>128</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-8596733192274108952.post-3490962351207711121</id><published>2012-02-28T15:02:00.001+02:00</published><updated>2012-02-29T20:02:47.075+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='Принципы разработки'/><category scheme='http://www.blogger.com/atom/ns#' term='ООП'/><category scheme='http://www.blogger.com/atom/ns#' term='Design by Contract'/><title type='text'>Принцип замещения Лисков и контракты</title><content type='html'>&lt;p align="justify"&gt;Идея этой заметки навеяна статьей Александра Бындю “&lt;a href="http://blog.byndyu.ru/2012/02/lsp.html"&gt;Дополнение к LSP&lt;/a&gt;” и может рассматриваться, как развернутый комментарий к статье Александра.  &lt;p align="justify"&gt;Итак, вопрос следующий, предположим, один из членов команды пытается реализовать интерфейс &lt;b&gt;IList&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;of&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;T&lt;/b&gt; в классе &lt;b&gt;DoubleList&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;of&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;T&lt;/b&gt; таким образом, чтобы при добавлении элемента с помощью метода &lt;b&gt;Add&lt;/b&gt;, добавлялся бы не один, а два одинаковых элемента. Поскольку класс &lt;b&gt;List&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;of&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;T&lt;/b&gt; всегда добавляет только один элемент, то можно считать, что данное поведение нарушает принцип замещения Лисков (LSP – Liskov Substitution Principle). &lt;/p&gt; &lt;a name='more'&gt;&lt;/a&gt; &lt;p align="justify"&gt;&lt;strong&gt;UPDATE&lt;/strong&gt;: Если вы вначале хотите познакомиться с определением принципа замещения Лисков, то оно приведено в конце статьи в разделе “Принцип замещения Лисков. Определение”.  &lt;p align="justify"&gt;Теперь давайте рассмотрим, так ли это, если речь идет о платформе .NET, ну и вообще поговорим о том, можем ли мы утверждать, что метод нарушает принцип замещения Лисков при отсутствии формальной спецификации того, что этот метод должен делать.  &lt;p align="justify"&gt;Для начала нужно внести небольшую правку в условие задачи (да, я понимаю, что это не честно, но в данном случае это оправдано). В исходной задаче говорилось о реализации классом &lt;b&gt;DoubleList&lt;/b&gt; интерфейса &lt;b&gt;IList&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;of&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;T&lt;/b&gt;, однако на самом деле в .NET Framework метод &lt;b&gt;Add&lt;/b&gt; объявлен в интерфейсе &lt;b&gt;ICollection&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;of&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;T&lt;/b&gt;, а значит, следует рассматривать, нарушает ли класс &lt;b&gt;DoubleList&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;of&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;T&lt;/b&gt; контракт &lt;i&gt;коллекции&lt;/i&gt;, а не &lt;i&gt;списка&lt;/i&gt;.  &lt;p align="justify"&gt;&lt;font color="#526e66"&gt;&lt;b&gt;ПРИМЕЧАНИЕ&lt;/b&gt;&lt;br&gt;Для простоты чтения кода и запуска примеров, я буду использовать не обобщенную версию класса &lt;b&gt;DoubleList&lt;/b&gt;, а специализированный контейнер для хранения строк, т.е. по сути, наш &lt;b&gt;DoubleList&lt;/b&gt; будет реализовать интерфейс &lt;b&gt;ICollection&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;of&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;string&lt;/b&gt;. &lt;/font&gt;&lt;/p&gt;&lt;pre style="line-height: normal; font-family: ; background: white"&gt;&lt;font face="Consolas"&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;class&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;DoubleList&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; : &lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;ICollection&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;lt;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;&amp;gt;&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;private&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;readonly&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;List&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;lt;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;gt; _backingList = &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;List&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;lt;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;&amp;gt;();&lt;br&gt; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;void&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; Add(&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; item)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Вместо добавления элемента один раз, добавляем его 2 раза &lt;/font&gt;&lt;/span&gt;&lt;br&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _backingList.Add(item);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _backingList.Add(item);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Остальные методы не важны &lt;/font&gt;&lt;/span&gt;&lt;br&gt;&lt;font color="#000000"&gt;}&lt;/font&gt;&lt;/font&gt;&lt;br&gt;&lt;/pre&gt;
&lt;p align="justify"&gt;Теперь, предположим, что где-то в коде мы используем интерфейс &lt;b&gt;ICollection&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;of&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;string&lt;/b&gt; (т.е. имеем полиморфное использование) и рассчитываем на то, что количество элементов увеличится строго на один. Подобное поведение можно выразить либо с помощью формального постусловия (например, с использованием Code Contract), либо можно выразить в виде юнит-теста. Поскольку к контрактам мы перейдем позже, то давайте рассмотрим вначале юнит-тест: &lt;pre style="line-height: normal; font-family: ; background: white"&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;[&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;Test&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;]&lt;/font&gt;&lt;br&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;void&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; TestAddMethodAddsOnlyOneElement()&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;ICollection&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;lt;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;gt; collection = &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;DoubleList&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;int&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; oldCount = collection.Count;&lt;br&gt; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; collection.Add(&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"foo"&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;Assert&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;.That(collection.Count, &lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;Is&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;.EqualTo(oldCount + 1));&lt;br&gt;}&lt;/font&gt;&lt;/font&gt;&lt;/pre&gt;
&lt;p align="justify"&gt;Да, действительно, этот тест будет нормально работать при использовании в качестве объекта &lt;b&gt;collection&lt;/b&gt; &lt;b&gt;List&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;of&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;string&lt;/b&gt;, и будет падать в случае использования &lt;b&gt;DoubleList&lt;/b&gt;&lt;b&gt;-а&lt;/b&gt;. 
&lt;p align="justify"&gt;Но вот только один вопрос: а вправе ли мы ожидать от метода &lt;b&gt;Add&lt;/b&gt; интерфейса &lt;b&gt;ICollection&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;of&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;T&lt;/b&gt; именно такого поведения? Когда речь заходит о корректности (проще говоря, о том, баг это или нет, причем не важно, где: в коде или дизайне), то она определяется лишь тем, соответствует ли поведение кода спецификации или нет. Спецификация может быть формальной или не формальной, но программа некорректна лишь тогда, когда она перестает делать то, для чего она предназначена, а если никто не знает, для чего она предназначена, то уже никто не может сказать, что она работает не правильно. 
&lt;p align="justify"&gt;Некоторое выражение, такое как &lt;b&gt;x&lt;/b&gt;&lt;b&gt; = &lt;/b&gt;&lt;b&gt;y&lt;/b&gt;&lt;b&gt; / 2&lt;/b&gt;, само по себе не является ни корректным, ни не корректным. Все зависит от того, каким должно быть отношение между &lt;b&gt;x&lt;/b&gt; и &lt;b&gt;y&lt;/b&gt; (подробнее об этом см. &lt;a href="http://sergeyteplyakov.blogspot.com/2010/05/design-by-contract.html"&gt;“Проектирование по контракту. Корректность ПО”&lt;/a&gt;). Ситуация с методом &lt;b&gt;Add&lt;/b&gt; аналогична: без формального или хотя бы неформального описания того, что должен делать этот метод мы не может утверждать правильно ли он реализован наследником. Если же мы откроем официальную документацию для &lt;a href="http://msdn.microsoft.com/en-us/library/63ywd54z.aspx"&gt;ICollection (Of T).Add Method&lt;/a&gt;, то все что мы увидим, это одну строчку, в которой будет сказано, что этот метод добавляет элемент в коллекцию. При этом все остальное (например, а точно ли он будет добавлен или в каком количестве), это лишь наши с вами допущения и единственное, что можно сделать, это постараться выяснить, как ведут себя другие реализации интерфейса &lt;b&gt;ICollection&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;of&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;T&lt;/b&gt;. 
&lt;p align="justify"&gt;Давайте добавим следующий тест, но на этот раз, будем проверять, что добавление двух одинаковых элементов, будет увеличивать количество элементов на 2. Понятное дело, что это более частный случай, по сравнению с добавлением одного элемента, но ведь, опять-таки, если вернуться к документации метода &lt;b&gt;Add&lt;/b&gt;, то там ни слова не говорится о том, что мы не можем добавлять одинаковые элементы; единственным ограничением является то, чтобы мы не добавляли элементы в коллекцию только для чтения. Других предусловий (в официальной документации они обычно выражаются в виде исключений) документация не содержит, а значит и никаких других проверок перед вызовом метода &lt;b&gt;Add&lt;/b&gt; делать не нужно: &lt;pre style="line-height: normal; font-family: ; background: white"&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;[&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;TestCaseSource&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"GetAllCollections"&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;)]&lt;/font&gt;&lt;br&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;void&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; TestAddToCollectionTwiceAddsTwoElement(&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;ICollection&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;lt;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;&amp;gt; collection)&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;int&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; oldCount = collection.Count;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;if&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; (collection.IsReadOnly)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;Console&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;.WriteLine(&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"Current collection type (&lt;/font&gt;&lt;/span&gt;&lt;span&gt;&lt;font color="#3cb371"&gt;{0}&lt;/font&gt;&lt;/span&gt;&lt;span&gt;&lt;font color="#a31515"&gt;) is readonly. Skipping it..."&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; collection.GetType());&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;return&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; collection.Add(&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"foo"&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; collection.Add(&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"foo"&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;Assert&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;.That(collection.Count, &lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;Is&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;.EqualTo(oldCount + 2));&lt;br&gt;}&lt;/font&gt;&lt;/font&gt;&lt;/pre&gt;
&lt;p align="justify"&gt;Еще раз обращаю внимание на проверку предусловия метода &lt;b&gt;Add&lt;/b&gt;, контракты – это не игра в одни ворота, поэтому мы должны выполнять свою часть договора, чтобы вызываемый код выполнял свою. Сам тест, является параметризованным (из состава NUnit), входные значения которого будут браться из метода &lt;b&gt;GetAllCollections&lt;/b&gt;. Я не будут заморачиваться с поиском всех существующих классов, реализующих интерфейс &lt;b&gt;ICollection&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;of&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;T&lt;/b&gt;, а просто добавлю некоторые популярные типы коллекций вручную: &lt;pre style="line-height: normal; font-family: ; background: white"&gt;&lt;font face="Consolas"&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;private&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;static&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;ICollection&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;lt;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;&amp;gt;[] GetAllCollections()&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;return&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;ICollection&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;lt;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;&amp;gt;[] &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;List&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;lt;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;&amp;gt;(), &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;HashSet&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;lt;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;&amp;gt;(), &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;Collection&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;lt;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;&amp;gt;(), &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;BindingList&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;lt;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;&amp;gt;(), &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;LinkedList&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;lt;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;&amp;gt;(), &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;ObservableCollection&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;lt;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;&amp;gt;(), &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;ReadOnlyCollection&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;lt;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;gt;(&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;List&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;lt;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;&amp;gt;()), &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;SortedSet&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;lt;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;&amp;gt;(), &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;[]{}, &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; };&lt;br&gt;}&lt;/font&gt;&lt;/font&gt;&lt;/pre&gt;
&lt;p align="justify"&gt;Теперь запускаем приведенный выше тест и видим, что как минимум два типа коллекций добавляют лишь один элемент, хотя мы просили добавить два. Это, конечно же, типы коллекций, которые не позволяют хранить дубликаты, такие как &lt;b&gt;HashSet&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;of&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;T&lt;/b&gt; и &lt;b&gt;SortedSet&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;of&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;T&lt;/b&gt;. 
&lt;p align="justify"&gt;А теперь давайте вернемся к исходному вопросу: &lt;b&gt;нарушает ли реализация метода &lt;/b&gt;&lt;b&gt;Add&lt;/b&gt;&lt;b&gt; классом &lt;/b&gt;&lt;b&gt;DoubleList&lt;/b&gt;&lt;b&gt; принцип замещения Лисков? &lt;/b&gt;Ответ: &lt;b&gt;Нет, не нарушает!&lt;/b&gt; 
&lt;p align="justify"&gt;Метод &lt;b&gt;Add&lt;/b&gt; интерфейса &lt;b&gt;ICollection&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;of&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;T&lt;/b&gt; не налагает никаких ограничений на то, какое количество элементов будет в коллекции после добавления, а значит, мы не можем требовать от всех классов, реализующих этот интерфейс, следовать определенному правилу. На самом деле, более правдоподобным (исходя из поведения коллекций, реализующих &lt;b&gt;ICollection&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;of&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;T&lt;/b&gt;) является следующее предусловие: после вызова метода &lt;b&gt;Add&lt;/b&gt;, следующий за ним вызов метода &lt;b&gt;Contains&lt;/b&gt; должен вернуть &lt;b&gt;true&lt;/b&gt; и количество элементов коллекции не должно &lt;b&gt;уменьшиться&lt;/b&gt; (т.е. newCount &amp;gt;= oldCount). 
&lt;p align="justify"&gt;В таком случае, если в нашем тесте заменить существующее утверждение на: &lt;pre style="line-height: normal; font-family: ; background: white"&gt;&lt;font face="Consolas"&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;Assert&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;.IsTrue(collection.Contains(&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"foo"&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;));&lt;/font&gt;&lt;/font&gt;&lt;br&gt;&lt;/pre&gt;
&lt;p align="justify"&gt;то все тесты будут проходить успешно. 
&lt;h5 align="justify"&gt;&lt;b&gt;Заключение&lt;/b&gt; &lt;/h5&gt;
&lt;p align="justify"&gt;Можно смело говорить о том, что &lt;b&gt;DoubleList&lt;/b&gt; не нарушает принцип замещения Лисков, либо нарушает вместе с некоторыми стандартными классами коллекций из BCL. С другой стороны, я согласен, что дизайн класса &lt;b&gt;DoubleList&lt;/b&gt; «кривоват», но не из-за нарушения некоторого принципа, понять формулировку которого в здравом уме практически невозможно (*), я бы скорее апеллировал к тому, что подобное поведение является интуитивно не понятным и наверняка приведет к проблемам в сопровождении. 
&lt;p align="justify"&gt;Не зря Мейер в своей толстенной книге (см. доп. ссылки) столь важную роль отводит формальной спецификации программ с помощью утверждений (предусловий, постусловий и инвариантов). Именно отсутствие формального описания того, что должен делать метод делает таким сложным написания наследника, который бы работал «правильно», поскольку что такое «правильно», никто сказать не может. Сигнатура метода (и возможный комментарий) являются слишком неформальными, чтобы понять «&lt;i&gt;изменится ли поведение при использовании наследника вместо базового класса&lt;/i&gt;». 
&lt;p align="justify"&gt;В следующий раз мы посмотрим, как &lt;b&gt;Code&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;Contracts&lt;/b&gt; (поддержка которых частично включена в состав mscorlib) могут помочь в решении нашей задачи, и о том, какое же формальное постусловие есть у метода &lt;b&gt;Add&lt;/b&gt;. 
&lt;p align="justify"&gt;------------------------- 
&lt;p align="justify"&gt;(*) Я отойду от обычного правила оформление сноски прямо в тексте и сделаю отдельный подраздел, посвященный определению принципа замещения Лисков. 
&lt;h5 align="justify"&gt;&lt;b&gt;Принцип замещения Лисков. Определение&lt;/b&gt; &lt;/h5&gt;
&lt;p align="justify"&gt;В замечательной книге Джеймса Коплиена «&lt;a href="http://sergeyteplyakov.blogspot.com/2009/07/c-cs.html"&gt;Программирование на языке С++&lt;/a&gt;» дается отличное, и совершенно непонятное определение принципа замещения Лисков: 
&lt;p align="justify"&gt;&lt;em&gt;...если для каждого объекта o1 типа S существует объект o2 типа T такой, что для всех программ P, определенных в терминах T, поведение P не изменяется при замене o2 на o1, то S является подтипом (subtype) для T.&lt;/em&gt;&lt;i&gt; &lt;/i&gt;
&lt;p align="justify"&gt;(&lt;em&gt;If for each object o1 of type S there is an object o2 of type T such that for all programs P deﬁned in terms of T, the behavior of P is unchanged when o1 is substituted for o2&amp;nbsp; then S is a subtype of T.&lt;/em&gt;) 
&lt;p align="justify"&gt;В целом, все хорошо, за исключением того, что совершенно не понятно, что означает «&lt;i&gt;в контексте Т&lt;/i&gt;» и совсем не понятно, что означает, «&lt;i&gt;поведение &lt;/i&gt;&lt;i&gt;P&lt;/i&gt;&lt;i&gt; &lt;/i&gt;&lt;i&gt;не изменится&lt;/i&gt;», когда это самое поведение нигде не описано. 
&lt;p align="justify"&gt;Другое популярное, но не более понятное определение, можно найти в &lt;a href="http://www.ozon.ru/context/detail/id/1573723/"&gt;книге Боба Мартина&lt;/a&gt;: 
&lt;p align="justify"&gt;&lt;i&gt;Должна быть возможность вместо базового типа подставить любое его подтип&lt;/i&gt;. 
&lt;p align="justify"&gt;Это определение проще, но едва ли яснее. В любом объектно-ориентированном языке программирования, существует неявное преобразование от наследника к базовому классу (при учете использования открытого наследования), таким образом, это требование стоит отнести скорее к компилятору или языку программированию, а не к классам определяемым пользователям. Сам Мартин в своей книге (а также в статье &lt;a href="http://www.objectmentor.com/resources/articles/lsp.pdf"&gt;The Liskov Substituion Principle&lt;/a&gt;) говорит о важности контрактов, и в частности, о важности предусловий и постусловий для спецификации поведения виртуальных методов. 
&lt;p align="justify"&gt;Контракты – это и есть та самая возможность спецификации &lt;i&gt;поведения &lt;/i&gt;&lt;i&gt;P&lt;/i&gt;, о котором говорится в исходном определении, и именно благодаря этой спецификации мы сможем гарантировать (или хотя бы понять), что поведение наследника соответствует поведению базового класса или интерфейса. 
&lt;h5 align="justify"&gt;&lt;b&gt;Дополнительные ссылки&lt;/b&gt; &lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;div align="justify"&gt;Бертран Мейер. &lt;a href="http://www.ozon.ru/context/detail/id/2336754/"&gt;Объектно-ориентированное конструирование программных систем&lt;/a&gt; &lt;/div&gt;
&lt;li&gt;
&lt;div align="justify"&gt;Роберт К. Мартин. &lt;a href="http://www.ozon.ru/context/detail/id/5800704/"&gt;Принципы, паттерны и методики гибкой разработки на языке C#&lt;/a&gt; &lt;/div&gt;
&lt;li&gt;
&lt;div align="justify"&gt;Robert C. Martin &lt;a href="http://www.objectmentor.com/resources/articles/lsp.pdf"&gt;The Liskov Substitution Principle&lt;/a&gt; &lt;/div&gt;
&lt;li&gt;
&lt;div align="justify"&gt;Robert C. Martin &lt;a href="http://www.objectmentor.com/resources/articles/Principles_and_Patterns.pdf"&gt;Design Principles and Patterns&lt;/a&gt; &lt;/div&gt;
&lt;li&gt;
&lt;div align="justify"&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2010/05/design-by-contract.html"&gt;Проектирование по контракту. О корректности ПО&lt;/a&gt; &lt;/div&gt;
&lt;li&gt;
&lt;div align="justify"&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2010/05/blog-post_17.html"&gt;Проектирование по контракту. Наследование&lt;/a&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8596733192274108952-3490962351207711121?l=sergeyteplyakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sergeyteplyakov.blogspot.com/feeds/3490962351207711121/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8596733192274108952&amp;postID=3490962351207711121' title='Комментарии: 9'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/3490962351207711121'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/3490962351207711121'/><link rel='alternate' type='text/html' href='http://sergeyteplyakov.blogspot.com/2012/02/lsp.html' title='Принцип замещения Лисков и контракты'/><author><name>Sergey Teplyakov</name><uri>https://profiles.google.com/108967431947412296254</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-2weHUILo1ns/AAAAAAAAAAI/AAAAAAAABRo/POab-anD6xA/s512-c/photo.jpg'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8596733192274108952.post-3834670636128039803</id><published>2012-02-13T20:12:00.001+02:00</published><updated>2012-02-14T22:30:15.153+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Философия программирования'/><category scheme='http://www.blogger.com/atom/ns#' term='ООП'/><category scheme='http://www.blogger.com/atom/ns#' term='Мысли'/><title type='text'>Повторное использование знаний</title><content type='html'>&lt;p align="justify"&gt;Уже не первый раз натыкаюсь на обсуждения вопросов о том, кто и как изучает новые технологии и о том, как справится с тем огромном потоком «нововведений», которые ежегодно появляются в индустрии ПО. Однажды я уже отвечал на этот &lt;a href="http://rsdn.ru/forum/philosophy/3646599.1.aspx"&gt;вопрос&lt;/a&gt; на кывт-е, и после &lt;a href="http://rsdn.ru/forum/dotnet/4576929.flat.aspx"&gt;очередного вопроса&lt;/a&gt; решил оформить эти мысли более структурированным образом.  &lt;p align="justify"&gt;Если оглядеться вокруг, то может сложиться впечатление, что отрасль разработки ПО шагает такими громадными шагами, что угнаться за ней нет никакой возможности. И если рассматривать всю отрасль в целом, то действительно это так и есть. Как-то сразу вспоминается старина Брукс со своим &lt;a href="http://sergeyteplyakov.blogspot.com/2009/02/blog-post.html"&gt;«Мифическим человеко-месяцем»&lt;/a&gt;, когда он в заключении к своей книге пишет о том, как изменилась индустрия ПО в середине 90-х по сравнению с 50-ми годами. В те далекие годы (да, 90-е тоже уже далеки, так что уж говорить за эпоху зарождения индустрии сорока годами ранее) можно было прочитать &lt;b&gt;все журналы (!)&lt;/b&gt;,компьютерной тематики, которые выходили в свет. Сейчас же ежемесячно появляется десятки книг только по одной из популярных технологий, а количество статей просто не поддается счету.  &lt;div align="justify"&gt; &lt;a name='more'&gt;&lt;/a&gt;&lt;/div&gt; &lt;p align="justify"&gt;С одной стороны, это заставляет относиться к выбору источников информации более осознанным и благоразумным образом. И именно эта мысль побудила меня к формированию списка &lt;a href="http://sergeyteplyakov.blogspot.com/2010/03/blog-post.html"&gt;наиболее интересных книг по программированию&lt;/a&gt; в целом, и отдельно, наиболее &lt;a href="http://sergeyteplyakov.blogspot.com/2011/04/cnet.html"&gt;значимых книг по языку C# и платформе .NET&lt;/a&gt;. Но помимо качественных источников информации (список книг хорошо было бы дополнить списком блогов, подкастов и видеоуроков) важным качеством любого программиста является еще структурирование и «повторное использование» полученных знаний.  &lt;p align="justify"&gt;По роду своей деятельности мы часто сталкиваемся с проблемами сложности. Мы привыкли бороться с ней самыми разными способами; мы создаем мелкие проверенные строительные блоки, на основе которых возводим крупные программные решения, мы абстрагируемся от несущественных деталей, пряча детали реализации за публичным интерфейсом классов или целых модулей, мы создаем иерархии классов для обобщения и повторного использования знаний и потраченных усилий.  &lt;p align="justify"&gt;А что, если попытаться провести параллели между общепринятыми практиками проектирования софта и нашими знаниями? Вот, например, декомпозиция задачи отлично применима, как в программировании, так и при обучении. Все мы прекрасно знаем, насколько легко сделать все сложно (простите за каламбур), если смешать в одном методе простенькую бизнес-логику и логику по работе с С-строками. Если методу требуется десяток более или менее простых операций со строками, то понять, что в нем происходит, будет очень сложно из-за всех этих strlen, strcpy, strcmp, strcat и многих других операций, в каждой из которых может крыться ошибка.  &lt;p align="justify"&gt;&lt;font color="#526e66"&gt;&lt;b&gt;ПРИМЕЧАНИЕ&lt;/b&gt;&lt;br&gt;Принято считать, что главное достоинство инкапсуляции – это &lt;b&gt;защита внутренней реализации класса от внешних клиентов&lt;/b&gt;; дескать, благодаря инкапсуляции, «поставщик услуг» можем изменить внутреннее поведение (или представление) не затронув при этом клиентов. Однако инкапсуляция – это игра не в одни ворота; помимо того, что класс сможет изменить свою реализацию, &lt;b&gt;инкапсуляция позволяет разгрузить клиента от ненужных деталей реализации&lt;/b&gt;, делая его (клиента) жизнь намного проще. &lt;/font&gt;&lt;/p&gt; &lt;p align="justify"&gt;Смешав в одном месте довольно простую логику вместе с плохо спроектированной абстракцией, мы получаем комбинаторный рост сложности, который взорвет мозг еще при написании кода, не говоря уже за его поддержку. То же самое можно заметить, например, при обсуждении некоторой задачи внутри команды, когда собеседник на тебя вываливает огромный объем информации, без большей части которой оба собеседника могли бы обойтись. При изучении чего-то нового эта же проблема проявляется даже более остро: &lt;i&gt;one&lt;/i&gt;&lt;i&gt; &lt;/i&gt;&lt;i&gt;step&lt;/i&gt;&lt;i&gt; &lt;/i&gt;&lt;i&gt;at&lt;/i&gt;&lt;i&gt; &lt;/i&gt;&lt;i&gt;a&lt;/i&gt;&lt;i&gt; &lt;/i&gt;&lt;i&gt;time&lt;/i&gt; – это любимый подход &lt;a href="http://sergeyteplyakov.blogspot.com/2012/01/18.html"&gt;Джона Скита&lt;/a&gt; при описании возможностей языка C#, которым он успешно пользуется в своей книге &lt;a href="http://sergeyteplyakov.blogspot.com/2011/04/c-in-depth-2nd-edition.html"&gt;“C# In Depth”&lt;/a&gt;, и именно этот подход делает его книгу простой для чтения и понимания.  &lt;p align="justify"&gt;&lt;a href="http://lh5.ggpht.com/-TlQyl3IIspc/TzlShYN--yI/AAAAAAAABa8/A0Tuhw0n3Bo/s1600-h/clip_image001%25255B5%25255D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="clip_image001" border="0" alt="clip_image001" src="http://lh4.ggpht.com/-b2zwjvr5Ueo/TzlSibAtmbI/AAAAAAAABbE/m6XYKE5L09k/clip_image001_thumb%25255B2%25255D.jpg?imgmax=800" width="266" height="375"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p align="center"&gt;Абстракции образуют иерархию (*)  &lt;p align="justify"&gt;(*) Это рисунок из книги Гради Буча &lt;a href="http://sergeyteplyakov.blogspot.com/2009/09/blog-post_15.html"&gt;«Объектно-ориентированный анализ и проектирование с примерами приложений»&lt;/a&gt;.  &lt;p align="justify"&gt;Теперь давайте рассмотрим еще один аспект разработки, который можно напрямую использовать и при обучении. По словам Гради Буча – любая сложная система является иерархичной. Именно иерархия и модульность позволяют хоть как-то справляться с невероятной сложностью моделируемых систем. И если рассмотреть любую современную технологию, то можно заметить, что количество слоев в ней будет огромным, и что каждый из них строится на основе хорошо проверенных слоях нижнего уровня.  &lt;h5 align="justify"&gt;Повторное использование знание на примере WCF&lt;/h5&gt; &lt;p align="justify"&gt;Давайте в качестве примера рассмотрим WCF.  &lt;p align="justify"&gt;&lt;a href="http://lh5.ggpht.com/-E3JJhVAuegQ/TzlT4jwN1nI/AAAAAAAABb8/6LS03e-QO5w/s1600-h/image%25255B4%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-2gOBiYFyKZ8/TzlT5ZZoHHI/AAAAAAAABcE/fhKyi6aQOXM/image_thumb%25255B2%25255D.png?imgmax=800" width="628" height="249"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p align="justify"&gt;Ни одна современная технология не является сферическим конем, построенным в вакууме от базовых концепций или других подобных технологий. На самом деле, большинство из них, это всего лишь &lt;b&gt;новая комбинация&lt;/b&gt;, хорошо проверенных и известных концепций, и WCF здесь – не исключение.  &lt;p align="justify"&gt;Современные технологии построения распределенных приложений довольно похожи. Все они строятся на основе проверенных паттернов, используют транспортные протоколы нижнего уровня и, в той или иной мере, борются с заблуждениями о распределенных приложениях (Fallacies of Distributed Computing).  &lt;p align="justify"&gt;Так, например, если у вас есть опыт работы с .NET Remoting-ом, то вы можете использовать его повторно при переходе на WCF: каждая из этих технологий является таким себе «слоеным» пирогом, с возможностью конфигурировать и настраивать самые разные уровни. В обеих технологиях мы можем использовать разные методы инстанцирования и конкурентности, настраивать безопасность и управлять сериализацией.  &lt;p align="justify"&gt;Знания низкоуровневых коммуникационных протоколов вы также сможете использовать повторно, поскольку рано или поздно в вашей системе начнутся проблемы, с которой не удастся разобраться без WireShark-а и анализа пакетов. Кроме того, многие проблемы или их решения могут быть заложены в самой природе транспортного протокола, и без этих знаний вы просто не сможете принять разумного решения: использовать ли в этой ситуации binding на основе протокола HTTP или TCP.  &lt;p align="justify"&gt;То же самое относится и к «сервисной» составляющей WCF. Сервисная архитектура – это отдельный аспект, который можно изучить за пределами WCF (например, используя веб-сервисы) и использовать его повторно уже в контексте новой технологии. Конечно, каждый из строительных блоков, на котором строится технология, может использоваться специфическим образом, но знание основополагающих принципов позволит разобраться в новой технологии на порядок легче.  &lt;p align="justify"&gt;&lt;font color="#526e66"&gt;&lt;b&gt;ПРИМЕЧАНИЕ&lt;/b&gt;&lt;br&gt;Однажды я попробовал ответить на вопрос, &lt;/font&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2011/02/wcf.html"&gt;&lt;font color="#526e66"&gt;«Что такое WCF?»&lt;/font&gt;&lt;/a&gt;&lt;font color="#526e66"&gt;, стараясь воспользоваться приведенным здесь способом повторного использования знаний и максимально использовать аналогии с приемниками WCF, такими как .NET Remoting и веб-сервисы. &lt;/font&gt;&lt;/p&gt; &lt;h5 align="justify"&gt;&lt;b&gt;Обобщение знаний на примере автоматического управления памятью&lt;/b&gt; &lt;/h5&gt; &lt;p align="justify"&gt;Помимо иерархичности и повторного использования низкоуровневых базовых блоков, мы можем воспользоваться еще одним механизмом ООП для борьбы со сложностью: обобщением и специализацией с помощью наследования.  &lt;p align="justify"&gt;Наследование – это одна из главных «визитных карточек» ООП и один из главных механизмов повторного использования, поэтому неудивительно, что именно им часто злоупотребляют. При проектировании ПО очень часто возникает проблема, под названием «&lt;b&gt;преждевременным обобщением&lt;/b&gt;» (premature generalization), когда на слишком ранних этапах разработки вводятся базовые классы с определенным поведением, хотя еще не понятно, что именно является «общим» в данном конкретном случае. Так, например, весьма часто можно увидеть иерархию из 5 базовых классов для простого класса &lt;b&gt;Customer&lt;/b&gt;, хотя на текущем этапе вовсе неясно, зачем это нужно.  &lt;p align="justify"&gt;&lt;font color="#526e66"&gt;&lt;b&gt;ПРИМЕЧАНИЕ&lt;/b&gt;&lt;br&gt;Причины такого положения дел в проектировании ПО очень хорошо описал Бертран Мейер: «Произвольно или нет, но многие учебные презентации создают впечатление, что структуру наследования следует проектировать от наиболее общего (верхней ее части) к более специфическим частям (листьям). В частности, это происходит потому, что лучший способ &lt;i&gt;описать&lt;/i&gt; существующую структуру – это идти от общего к частному, от фигур к замкнутым фигурам, затем к многоугольникам, прямоугольникам, квадратам. Но лучший способ описания структуры вовсе не означает, что он является и лучшим способом ее &lt;i&gt;создания&lt;/i&gt;.&lt;br&gt;В идеальном мире, населенном совершенными людьми, мы бы сразу же обнаруживали правильные абстракции, выводили бы из них категории, затем их подкатегории и так далее. &lt;i&gt;В реальном мире, однако, мы часто вначале обнаруживаем частный случай и лишь потом открываем общую абстракцию&lt;/i&gt;.» &lt;/font&gt;&lt;/p&gt; &lt;p align="justify"&gt;Точно также, довольно типичной является ситуация, когда собеседник обобщает свои опыт и знания, полученные на одном проекте с одним языком программирования, на другие проекты и другие языки программирования. По сути, он обобщает эти знания в «базовые» классы своих знаний, хотя на данный момент у этих классов есть только один «наследник»:  &lt;p align="justify"&gt;&lt;i&gt;Я: Я знаю, что множественное наследование – это фигня! &lt;br&gt;Он: Почему? &lt;br&gt;Я: Потому, что его нет в &lt;/i&gt;&lt;i&gt;Java&lt;/i&gt;&lt;i&gt;, а других языков я не знаю! &lt;/i&gt;&lt;/p&gt; &lt;p align="justify"&gt;Довольно опрометчиво делать подобные выводы, если единственный язык, который я знаю – это Java. Скорее всего, я смогу сделать более правильные выводы и структурировать свои знания более осознанно, познакомившись хотя бы с несколькими языками, поддерживающими множественное наследование, такими как С++ и Eiffel.  &lt;p align="justify"&gt;Давайте, в качестве примера, рассмотрим несколько способов автоматического управления памяти и представим их в иерархической форме.  &lt;p align="justify"&gt;&lt;a href="http://lh4.ggpht.com/-Q_1agvIteq8/TzlT6bMxkHI/AAAAAAAABcM/B2oA_QForxE/s1600-h/image%25255B9%25255D.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/-285dKQrBfhg/TzlT7Zeiz-I/AAAAAAAABcU/J4rGO5jLx6c/image_thumb%25255B5%25255D.png?imgmax=800" width="595" height="310"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p align="justify"&gt;Автоматическое управление памятью является наиболее общим понятием, под который попадает несколько частных случаев. Так, например, можно выделить два наиболее типичных решения, основанных (1) на сборке мусора (garbage collection) и (2) на подсчете ссылок; при этом сборщики мусора могут использовать поколения (generations) или нет.  &lt;p align="justify"&gt;Каждое из решений обладает своими плюсами и минусами. Так, управление памятью на основе подсчета ссылок будет страдать от «кольцевых» зависимостей (когда объекта А содержит ссылку на объект B, тот содержит ссылку на объект C, а он опять ссылается на А), но при этом будет обеспечивать детерминированность очистки ресурсов. Сборщик мусора же решит проблему с циклическими ссылками, но расплачиваться за это придется отсутствием детерминированности освобождения ресурсов.  &lt;p align="justify"&gt;При изучении разных языков программирования, можно стараться запомнить, как именно реализовано автоматическое управление памятью в каждом из них, но после изучения нескольких языков эффективнее будет обобщить эти знания и использовать их повторно. Конечно, каждая платформа или язык программирования обладают своими особенностями, но знания основных механизмов управления памятью существенно сократят кривую обучения, ведь понять и запомнить придется лишь различия, а не весь механизм целиком. Именно поэтому «прагматики» Дэйв Томас и Энди Хант в своей книге &lt;a href="http://sergeyteplyakov.blogspot.com/2009/09/blog-post.html"&gt;«Программист-прагматик. Путь от подмастерья к мастеру»&lt;/a&gt; советовали изучать по одному языку программированию каждый год и знакомиться с другими платформами и операционными системами. Подобное расширение кругозора позволит шире смотреть на новые задачи, а также позволит обобщить и структурировать уже существующие знания.  &lt;p align="justify"&gt;&lt;b&gt;Заключение&lt;/b&gt;  &lt;p align="justify"&gt;&lt;a href="http://lh6.ggpht.com/-nIAEfnu7Dag/TzlSneHXJpI/AAAAAAAABbs/iF9t-Oirvrc/s1600-h/clip_image006%25255B5%25255D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="clip_image006" border="0" alt="clip_image006" src="http://lh6.ggpht.com/-lG1agBNYY8I/TzlSojbWpSI/AAAAAAAABb0/1xtQaKf3TOo/clip_image006_thumb%25255B2%25255D.jpg?imgmax=800" width="441" height="263"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p align="justify"&gt;Сколько бы «новых» технологий не выходило бы в свет, сколько бы новых уровней абстракции ни изобретали, знание «основ» всегда будет полезным. Значительно проще понять несколько основополагающих принципов, вместо изучения сотни, казалось бы, несвязанных фактов. Знание низкоуровневых концепций можно использовать повторно, что значительно сократит время изучения «новых технологий», поскольку по большей части, они таковыми не являются. Кроме того, рано или поздно &lt;a href="http://russian.joelonsoftware.com/Articles/LeakyAbstractions.html"&gt;«абстракции дадут течь»&lt;/a&gt; и чтобы ее устранить, придется понять, как же она устроена внутри.    &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8596733192274108952-3834670636128039803?l=sergeyteplyakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sergeyteplyakov.blogspot.com/feeds/3834670636128039803/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8596733192274108952&amp;postID=3834670636128039803' title='Комментарии: 0'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/3834670636128039803'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/3834670636128039803'/><link rel='alternate' type='text/html' href='http://sergeyteplyakov.blogspot.com/2012/02/blog-post.html' title='Повторное использование знаний'/><author><name>Sergey Teplyakov</name><uri>https://profiles.google.com/108967431947412296254</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-2weHUILo1ns/AAAAAAAAAAI/AAAAAAAABRo/POab-anD6xA/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/-b2zwjvr5Ueo/TzlSibAtmbI/AAAAAAAABbE/m6XYKE5L09k/s72-c/clip_image001_thumb%25255B2%25255D.jpg?imgmax=800' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8596733192274108952.post-5840341174891671426</id><published>2012-02-06T21:23:00.001+02:00</published><updated>2012-02-06T22:00:16.916+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WPF'/><title type='text'>Attached свойства для ограничения текстового ввода</title><content type='html'>&lt;p align="justify"&gt;WPF – это уже далеко не новая технология на рынке, но относительно новая для меня. И, как это часто бывает при изучении чего-то нового, появляется желание/необходимость в изобретении велосипедов с квадратными колесами и литыми дисками для решения некоторых типовых задач.&lt;/p&gt; &lt;p align="justify"&gt;Одной из таких задач является ограничение ввода пользователем определенных данных. Например, мы хотим, чтобы в некоторое текстовое поле можно было вводить только целочисленные значения, а в другое – дату в определенном формате, а в третье – только числа с плавающей запятой. Конечно, окончательная валидация подобных значений все равно будет происходить во вью-моделях, но подобные ограничения на ввод делают пользовательский интерфейс более дружественным.&lt;/p&gt; &lt;p align="justify"&gt;В Windows Forms эта задача решалась довольно легко, а когда в распоряжении был тот же TextBox от DevExpress со встроенной возможностью ограничения ввода с помощью регулярных выражений, то все было вообще просто. Примеров решения этой же задачи в WPF &lt;a href="http://lurkmore.to/%D0%A1%D1%82%D0%BE%D0%BF%D0%B8%D1%86%D0%BE%D1%82"&gt;довольно много&lt;/a&gt;, большинство из которых сводится к одному из двух вариантов: использование наследника класса &lt;a href="http://msdn.microsoft.com/en-us/library/system.windows.controls.textbox.aspx"&gt;TextBox&lt;/a&gt; или добавление &lt;a href="http://msdn.microsoft.com/en-us/library/ms749011.aspx"&gt;attached property&lt;/a&gt; с нужными ограничениями.&lt;/p&gt; &lt;a name='more'&gt;&lt;/a&gt; &lt;p align="justify"&gt;&lt;font color="#526e66"&gt;&lt;strong&gt;ПРИМЕЧАНИЕ&lt;br&gt;&lt;/strong&gt;Если вам не очень интересны мои рассуждения, а нужны сразу же примеры кода, то вы можете либо скачать весь проект &lt;/font&gt;&lt;a href="https://github.com/SergeyTeplyakov/WpfEx"&gt;&lt;font color="#526e66"&gt;WpfEx с GitHub&lt;/font&gt;&lt;/a&gt;&lt;font color="#526e66"&gt;, либо же скачать основную реализацию, которая содержится в &lt;/font&gt;&lt;a href="https://github.com/SergeyTeplyakov/WpfEx/blob/master/WpfEx/AttachedBehaviors/TextBoxBehavior.cs"&gt;&lt;font color="#526e66"&gt;TextBoxBehavior.cs&lt;/font&gt;&lt;/a&gt;&lt;font color="#526e66"&gt; и &lt;/font&gt;&lt;a href="https://github.com/SergeyTeplyakov/WpfEx/blob/master/WpfEx/AttachedBehaviors/TextBoxDoubleValidator.cs"&gt;&lt;font color="#526e66"&gt;TextBoxDoubleValidator.cs&lt;/font&gt;&lt;/a&gt;&lt;font color="#526e66"&gt;.&lt;/font&gt;&lt;/p&gt; &lt;h5 align="justify"&gt;Ну что, приступим?&lt;/h5&gt; &lt;p align="justify"&gt;Поскольку наследование вводит довольно жесткое ограничение, то лично мне в этом случае больше нравится использование attached свойств, благо этот механизм позволяет ограничить применение этих свойств к элементам управления определенного типа (я не хочу, чтобы это attached свойство &lt;strong&gt;IsDouble&lt;/strong&gt; можно было применить к TextBlock-у для которого это не имеет смысла).&lt;/p&gt; &lt;p align="justify"&gt;Кроме того нужно учесть, что&amp;nbsp; при ограничении ввода пользователя нельзя использовать какие-то конкретные разделители целой и дробной части (такие как ‘.’ (точка) или ‘,’ (запятая)), а также знаки ‘+’ и ‘-‘, поскольку все это зависит от региональных настроек пользователя.&lt;/p&gt; &lt;p align="justify"&gt;Чтобы реализовать возможность ограничения ввода данных, нам нужно перехватить событие ввода данных пользователем внучную, проанализировать его и отменить эти изменения, если они нам не подходят. В отличие от Windows Forms, в котором принято использование пары событий &lt;strong&gt;XXXChanged&lt;/strong&gt; и &lt;strong&gt;XXXChanging&lt;/strong&gt;, в WPF для этих же целей используются Preview версии событий, которые могут быть обработаны таким образом, чтобы основное событие не срабатывало.&amp;nbsp; (Классическим примером может служить обработка событий от мыши или клавиатуры, запрещающие некоторые клавиши или их комбинации).&lt;/p&gt; &lt;p align="justify"&gt;И все было бы хорошо, если бы класс &lt;a href="http://msdn.microsoft.com/en-us/library/system.windows.forms.textbox.aspx"&gt;TextBox&lt;/a&gt; вместе с событием &lt;a href="http://msdn.microsoft.com/en-us/library/system.windows.forms.control.textchanged.aspx"&gt;TextChanged&lt;/a&gt; содержал бы еще и &lt;strong&gt;PreviewTextChanged&lt;/strong&gt;, которое можно было бы обработать и «прервать» ввод данных пользователем, если мы считаем вводимый текст некорректным. А поскольку его нет, то и приходится всем и каждому свой лисапет изобретать.&lt;/p&gt; &lt;h5 align="justify"&gt;Решение задачи&lt;/h5&gt; &lt;p align="justify"&gt;Решение задачи сводится к созданию класса TextBoxBehavior, содержащего attached свойство IsDoubleProperty, после установки которого пользователь не сможет вводить в данное текстовое поле ничего кроме символов +, -, . (разделителя целой и дробной части), а также цифр (не забываем, что нам нужно использовать настройки текущего потока, а не захардкодженные значения).&lt;/p&gt;&lt;pre style="line-height: normal; font-family: ; background: white"&gt;&lt;font face="Consolas"&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;class&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;TextBoxBehavior&lt;/font&gt;&lt;/span&gt;
&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Attached свойство булевого типа, установка которого приведет к &lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// ограничению вводимых пользователем данных&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;static&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;readonly&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; DependencyProperty IsDoubleProperty = &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DependencyProperty.RegisterAttached( &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"IsDouble"&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;, &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;typeof&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; (&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;bool&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;),&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;typeof&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; (TextBoxBehavior),&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; FrameworkPropertyMetadata(&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;false&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;, OnIsDoubleChanged));&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;    // Данный атрибут не позволит использовать IsDouble ни с какими другими &lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// UI элементами, кроме TextBox или его наследников&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; [AttachedPropertyBrowsableForType(&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;typeof&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; (TextBox))]&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;static&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;bool&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; GetIsDouble(DependencyObject element)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {}&lt;br&gt;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;    public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;static&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;void&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; SetIsDouble(DependencyObject element, &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;bool&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; value)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {}&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;    // Вызовется, при установке TextBoxBehavior.IsDouble="True" в XAML-е&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;private&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;static&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;void&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; OnIsDoubleChanged(DependencyObject d,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DependencyPropertyChangedEventArgs e)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Уличная магия&lt;br&gt;&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
}&lt;/font&gt;&lt;/font&gt;&lt;/pre&gt;
&lt;p align="justify"&gt;Следующим, и основным этапом является реализация той «основной магии», о которой говорится в комментарии в методе &lt;strong&gt;OnIsDoubleChanged&lt;/strong&gt;.&lt;/p&gt;
&lt;p align="justify"&gt;Основная сложность реализации этого метода состоит в следующем:&amp;nbsp; во-первых, нам нужно понять, что является валидными данными, а что нужно отсекать, и во-вторых, нам нужно каким-то образом получить «вводимый» пользователем текст, поскольку готового события, типа &lt;strong&gt;PreviewTextChanged&lt;/strong&gt; в нашем распоряжении нет.&lt;/p&gt;
&lt;h5 align="justify"&gt;Формирование измененной строки&lt;/h5&gt;
&lt;p align="justify"&gt;Для получения строки, вводимой пользователем в методе &lt;strong&gt;OnIsDoubleChanged&lt;/strong&gt; нужно подписаться на два события: (1) &lt;strong&gt;PreviewTextInput&lt;/strong&gt; и (2) перехватить событие «вставки» текста из буфера обмена:&lt;/p&gt;&lt;pre style="line-height: normal; font-family: ; background: white"&gt;&lt;font face="Consolas"&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Вызовется, при установке TextBoxBehavior.IsDouble="True" в XAML-е&lt;/font&gt;&lt;/span&gt;
&lt;span&gt;&lt;font color="#0000ff"&gt;private&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;static&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;void&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; OnIsDoubleChanged(DependencyObject d,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; DependencyPropertyChangedEventArgs e)&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Поскольку мы ограничили наше attached свойство только классом&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// TextBox или его наследниками, то следующее преобразование - безопасно&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;var&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; textBox = (TextBox) d;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;    // Теперь нам нужно обработать два важных слчая:&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// 1. Ручной ввод данных пользователем&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// 2. Вставка данных из буфера обмена&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; textBox.PreviewTextInput += PreviewTextInputForDouble;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; DataObject.AddPastingHandler(textBox, OnPasteForDouble);
}&lt;/font&gt;&lt;/font&gt;&lt;/pre&gt;
&lt;p align="justify"&gt;Основная сложность реализации обработчика &lt;strong&gt;PreviewTextInput&lt;/strong&gt; (как и события вставки текста из буфера обмена) заключается в том, что в аргументах события передается не суммарное значение текста, а лишь вновь введенная его часть. Поэтому суммарный текст нужно формировать вручную, учитывая при этом возможность выделения текста в TextBox-е, текущее положение курсора в нем и, возможно, состояние кнопки Insert (которое мы анализировать не будем):&lt;/p&gt;&lt;pre style="line-height: normal; font-family: ; background: white"&gt;&lt;font face="Consolas"&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;private&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;static&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;void&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; PreviewTextInputForDouble(&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;object&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; sender,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; TextCompositionEventArgs e)
{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// e.Text содержит только новый текст, так что без текущего &lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// состояния TextBox-а не обойтись&lt;br&gt;&lt;/font&gt;&lt;/span&gt;
&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;var&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; textBox = (TextBox)sender; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; fullText; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;    // Если TextBox содержит выделенный текст, то заменяем его на e.Text&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;if&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; (textBox.SelectionLength &amp;gt; 0)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; fullText = textBox.Text.Replace(textBox.SelectedText, e.Text);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;else&lt;/font&gt;&lt;/span&gt;
&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Иначе нам нужно вставить новый текст в позицию курсора&lt;/font&gt;&lt;/span&gt;
&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; fullText = textBox.Text.Insert(textBox.CaretIndex, e.Text);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;    // Теперь валидируем полученный текст&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;bool&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; isTextValid = TextBoxDoubleValidator.IsValid(fullText);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;    // И предотвращаем событие TextChanged если текст невалиден&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; e.Handled = !isTextValid;
}&lt;/font&gt;&lt;/font&gt;&lt;/pre&gt;
&lt;h5 align="justify"&gt;Класс TextBoxDoubleValidator&lt;/h5&gt;
&lt;p align="justify"&gt;Вторым важным моментом является реализация логики валидации вновь введенного текста, ответственность за которую отведена методу &lt;strong&gt;IsValid&lt;/strong&gt; отдельного класса &lt;strong&gt;TextBoxDoubleValidator&lt;/strong&gt;.&lt;/p&gt;
&lt;p align="justify"&gt;Самым простым способом понять, как должен вести себя метод &lt;strong&gt;IsValid&lt;/strong&gt; этого класса, это написать для него юнит-тест, который покроет все corner case-ы (это как раз один из тех случаев, когда параметризованные юнит-тесты рулят со страшной силой):&lt;/p&gt;
&lt;p align="justify"&gt;&lt;font color="#526e66"&gt;&lt;strong&gt;ПРИМЕЧАНИЕ&lt;/strong&gt;&lt;br&gt;Это как раз тот случай, когда юнит тест – это не просто тест, проверяющий корректность реализации определенной функциональности. Это как раз тот случай, о котором неоднократно говорил Кент Бек, описывая accountability; прочитав этот тест можно понять, о чем думал разработчик метода валидации, «повторно использовать» его знания и найти ошибки в его рассуждениях и, тем самым, вероятно и в коде реализации. Это не просто набор тестов – это важная часть спецификации этого метода!&lt;/font&gt;&lt;/p&gt;&lt;pre style="line-height: normal; font-family: ; background: white"&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;[&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;TestCase&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;""&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;, Result = &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;true&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;)]&lt;br&gt;[&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;TestCase&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"."&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;, Result = &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;true&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;)]&lt;br&gt;[&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;TestCase&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"-."&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;, Result = &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;true&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;)]&lt;br&gt;[&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;TestCase&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"-.1"&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;, Result = &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;true&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;)]&lt;br&gt;[&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;TestCase&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"+"&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;, Result = &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;true&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;)]&lt;br&gt;[&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;TestCase&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"-"&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;, Result = &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;true&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;)]&lt;br&gt;[&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;TestCase&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;".0"&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;, Result = &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;true&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;)]&lt;br&gt;[&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;TestCase&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"1.0"&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;, Result = &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;true&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;)]&lt;br&gt;[&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;TestCase&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"+1.0"&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;, Result = &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;true&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;)]&lt;br&gt;[&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;TestCase&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"-1.0"&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;, Result = &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;true&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;)]&lt;br&gt;[&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;TestCase&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"001.0"&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;, Result = &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;true&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;)]&lt;br&gt;[&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;TestCase&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;" "&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;, Result = &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;false&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;)]&lt;br&gt;[&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;TestCase&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;".."&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;, Result = &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;false&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;)]&lt;br&gt;[&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;TestCase&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"..1"&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;, Result = &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;false&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;)]&lt;br&gt;[&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;TestCase&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"1+0"&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;, Result = &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;false&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;)]&lt;br&gt;[&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;TestCase&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"1.a"&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;, Result = &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;false&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;)]&lt;br&gt;[&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;TestCase&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"1..1"&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;, Result = &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;false&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;)]&lt;br&gt;[&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;TestCase&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"a11"&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;, Result = &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;false&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;)]&lt;br&gt;[&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;SetCulture&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"en-US"&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;)]&lt;/font&gt;&lt;br&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;bool&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; TestIsTextValid(&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; text)&lt;br&gt;{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;bool&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; isValid = &lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;TextBoxDoubleValidator&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;.IsValid(text);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;Console&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;.WriteLine(&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"'&lt;/font&gt;&lt;/span&gt;&lt;span&gt;&lt;font color="#3cb371"&gt;{0}&lt;/font&gt;&lt;/span&gt;&lt;span&gt;&lt;font color="#a31515"&gt;' is &lt;/font&gt;&lt;/span&gt;&lt;span&gt;&lt;font color="#3cb371"&gt;{1}&lt;/font&gt;&lt;/span&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;, text, isValid ? &lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"valid"&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; : &lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"not valid"&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;return&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; isValid;&lt;br&gt;}&lt;/font&gt;&lt;/font&gt;&lt;/pre&gt;
&lt;p align="justify"&gt;Тестовый метод возвращает &lt;strong&gt;true&lt;/strong&gt;, если параметр &lt;strong&gt;text&lt;/strong&gt; является валидным, и это означает, что соответствующий текст можно будет вбить в &lt;strong&gt;TextBox&lt;/strong&gt; с attached свойством IsDouble. Обратите внимание на несколько моментов: (1) использование атрибута &lt;strong&gt;SetCulture&lt;/strong&gt;, который устанавливает нужную локаль и (2) на некоторые входные значения, такие значения как “-.”, которые не являются корректными значениями для типа &lt;strong&gt;Double&lt;/strong&gt;. &lt;/p&gt;
&lt;p align="justify"&gt;Явная установка локали нужна для того, чтобы тесты не падали у разработчиков с другими персональными настройками, ведь в русской локали, в качестве разделителя используется символ ‘,’ (запятая), а в американской – ‘.’ (точка). Такой странный текст, как “-.” является корректным, поскольку мы должны пользователю завершить ввод, если он хочет ввести строку “-.1”, которая является корректным значением для &lt;strong&gt;Double&lt;/strong&gt;. (Интересно, что на StackOverflow для решения этой задачи очень часто советуют просто использовать &lt;a href="http://msdn.microsoft.com/ru-ru/library/system.double.tryparse.aspx"&gt;Double.TryParse&lt;/a&gt;, который явно не будет работать в некоторых случаях).&lt;/p&gt;
&lt;p align="justify"&gt;&lt;font color="#526e66"&gt;&lt;strong&gt;ПРИМЕЧАНИЕ&lt;/strong&gt;&lt;br&gt;Я не хочу захламлять статью деталями реализации метода &lt;strong&gt;IsValid&lt;/strong&gt;, хочу лишь отметить использование в теле этого метода &lt;a href="http://msdn.microsoft.com/en-us/library/dd642243.aspx"&gt;ThreadLocal&amp;lt;T&amp;gt;&lt;/a&gt;, который позволяет получить и закэшировать &lt;strong&gt;DoubleSeparator&lt;/strong&gt; локальный для каждого потока. Полную реализацию метода &lt;strong&gt;TextBoxDoubleValidator.IsValid&lt;/strong&gt; можно найти &lt;a href="https://github.com/SergeyTeplyakov/WpfEx/blob/master/WpfEx/AttachedBehaviors/TextBoxDoubleValidator.cs"&gt;здесь&lt;/a&gt;, более подробную информацию об &lt;strong&gt;ThreadLocal&amp;lt;T&amp;gt;&lt;/strong&gt; можно почитать у Джо Албахари в статье &lt;a href="http://sergeyteplyakov.blogspot.com/2010/08/c-3.html"&gt;Работа с потоками. Часть 3&lt;/a&gt;.&lt;/font&gt;&lt;/p&gt;
&lt;h5 align="justify"&gt;Альтернативные решения&lt;/h5&gt;
&lt;p align="justify"&gt;Помимо перехвата событий &lt;strong&gt;PreviewTextInput&lt;/strong&gt; и вставки текста из буфера обмена существуют и другие решения. Так, например, я встречал попытку решить эту же задачу путем перехвата события &lt;strong&gt;PreviewKeyDown&lt;/strong&gt; с фильтрацией всех клавиш кроме цифровых. Однако это решение сложнее, поскольку все равно придется заморачиваться с «суммарным» состоянием TextBox-а, да и чисто теоретически, разделителем целой и дробной части может быть не один символ, а целая строка (&lt;a href="http://msdn.microsoft.com/en-us/library/system.globalization.numberformatinfo.numberdecimalseparator.aspx"&gt;NumberFormatInfo.NumberDecimalSeparator&lt;/a&gt; возвращает &lt;strong&gt;string&lt;/strong&gt;, а не &lt;strong&gt;char&lt;/strong&gt;).&lt;/p&gt;
&lt;p align="justify"&gt;Есть еще вариант в событии &lt;strong&gt;KeyDown&lt;/strong&gt; сохранить предыдущее состояние &lt;strong&gt;TextBox.Text&lt;/strong&gt;, а в событии &lt;strong&gt;TextChanged&lt;/strong&gt; вернуть ему старое значение, если новое значение не устраивает. Но это решение выглядит неестественным, да и реализовать его с помощью attached свойств будет не так-то просто.&lt;/p&gt;
&lt;h5 align="justify"&gt;Заключение&lt;/h5&gt;
&lt;p align="justify"&gt;Когда мы в последний раз обсуждали с коллегами отсутствие в WPF полезных и весьма типовых возможностей, то мы пришли к выводу, что этому есть объяснение, и в этом есть и положительная сторона. Объяснение сводится к тому, что от &lt;a href="http://sergeyteplyakov.blogspot.com/2011/10/blog-post_26.html"&gt;закона дырявых абстракций&lt;/a&gt; никуда не деться и WPF будучи «абстракцией» весьма сложной, течет как решето. Полезная же сторона заключается в том, что отсутствие некоторых полезных возможностей заставляет нас иногда думать (!) и не забывать о том, что мы программисты, а не умельцы копи-пасты. &lt;/p&gt;
&lt;p align="justify"&gt;Напомню, что полную реализацию классов приведенных классов, примеры их использования и юнит-тесты можно найти на &lt;a href="https://github.com/SergeyTeplyakov/WpfEx"&gt;github&lt;/a&gt;.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8596733192274108952-5840341174891671426?l=sergeyteplyakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sergeyteplyakov.blogspot.com/feeds/5840341174891671426/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8596733192274108952&amp;postID=5840341174891671426' title='Комментарии: 5'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/5840341174891671426'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/5840341174891671426'/><link rel='alternate' type='text/html' href='http://sergeyteplyakov.blogspot.com/2012/02/attached-properties.html' title='Attached свойства для ограничения текстового ввода'/><author><name>Sergey Teplyakov</name><uri>https://profiles.google.com/108967431947412296254</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-2weHUILo1ns/AAAAAAAAAAI/AAAAAAAABRo/POab-anD6xA/s512-c/photo.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8596733192274108952.post-5957454856878752546</id><published>2012-01-30T21:33:00.001+02:00</published><updated>2012-01-30T21:33:43.775+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Юмор'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>18 фактов о Джоне Ските</title><content type='html'>&lt;p align="justify"&gt;Практически на каждом своем выступлении, будь то &lt;a href="http://sergeyteplyakov.blogspot.com/2011/10/blog-post.html"&gt;семинар&lt;/a&gt; или воркшоп, рано или поздно заходит речь об известных представителей .NET community и одним из первых в этом списке идет Джон Скит (Jon Skeet), гуру stackoverflow.com и автор одной из самых интересных книг по языку C# - &lt;a href="http://sergeyteplyakov.blogspot.com/2011/04/c-in-depth-2nd-edition.html"&gt;“C# in Depth”&lt;/a&gt;.  &lt;p align="justify"&gt;Чтобы рассказать о том, кто есть Джон и что он сделал для индустрии, достаточно привести о нем несколько фактов. Многие слышали &lt;a href="http://ru.wikipedia.org/wiki/%D0%A4%D0%B0%D0%BA%D1%82%D1%8B_%D0%BE_%D0%A7%D0%B0%D0%BA%D0%B5_%D0%9D%D0%BE%D1%80%D1%80%D0%B8%D1%81%D0%B5"&gt;факты о Чаке Норрисе&lt;/a&gt;, такие как «Чак Норрис досчитал до бесконечности. Дважды» или что «Чак Норрис единственный человек, который обыграл стену в теннис». Но далеко не все знают о том, что подобные факты есть и о Джоне Ските (сам факт существования которых уже о многом говорит). &lt;/p&gt; &lt;a name='more'&gt;&lt;/a&gt; &lt;p align="justify"&gt;Итак, вот они:  &lt;blockquote&gt; &lt;p align="justify"&gt;1. Джон Скит может делить на 0.&lt;br&gt;2. Если код Джона Скита не компилируется, то компилятор извиняется за это.&lt;br&gt;3. Если поискать в Гугле “guru”, то Гугл выдает: «Возможно, вы имели ввиду: Джон Скит?»&lt;br&gt;4. Джон Скит ради интереса написал последний проект полностью в MS Paint.&lt;br&gt;5. Джон Скит не следует стандартам кодирования, его код &lt;i&gt;и есть &lt;/i&gt;стандарт кодирования.&lt;br&gt;6. Однажды Джон Скит ответил на вопрос за 42 секунды &lt;i&gt;до того&lt;/i&gt;, как он был задан.&lt;br&gt;7. Джон Скит неизменяем. Если что-то должно измениться, то изменяется вся вселенная.&lt;br&gt;8. Джон Скит уже написал книгу по C# 6.0. Через три года Андерс Хейлсберг прочитает ее, чтобы выяснить, все ли правильно поняла команда разработчиков языка C#.&lt;br&gt;9. Джон Скит единственный человек из топ 100 пользователей SO. Остальные – это боты, написанные Джоном, чтобы как-то развеяться между ответами на вопросы.&lt;br&gt;A. &lt;a href="http://en.wikipedia.org/wiki/Dining_philosophers_problem"&gt;«Обедающие философы»&lt;/a&gt; ждут, пока доест Джон Скит.&lt;br&gt;B. Когда Джон Скит указывает на null, null дрожит от страха.&lt;br&gt;C. Если Джон Скит публикует дубликат на StackOverflow, &lt;b&gt;то исходный вопрос закрывается, как дубликат&lt;/b&gt;.&lt;br&gt;D. При вызове метода обратного вызова Джона Скита, CLR добавляет «пожалуйста».&lt;br&gt;E. Джон Скит не пользуется отладчиком. В его программах не бывает ошибок.&lt;br&gt;F. CLR использует Just-In-Time компиляцию, потому что каждая машинная инструкция должна быть одобрена Джоном Скитом.&lt;br&gt;10. Когда выполняется программа Джона Скита, сборщик мусора отдыхает. Его объекты сами знают, когда самоуничтожиться.&lt;br&gt;11. Мозг Джона Скита работает в двоичном режиме.&lt;br&gt;12. Изначально сайт StackOverflow.com назывался JonSkeet.com и был переименован только благодаря скромности Джона Скита.&lt;br&gt;13. У Джона Скита не бывает проблем с производительностью. Он заставляет всю вселенную ждать своей очереди.&lt;br&gt;14. Джон Скит может выполнить бесконечный цикл за 1.55 секунды.&lt;br&gt;15. Джон Скит не отвечает на вопросы на StackOverflow, он смотрит на них и они сами находят ответ.&lt;br&gt;16. Потоки Джона Скита никогда не спят. Они выжидают.&lt;br&gt;17. Если Интернет – это паутина, то Джон Скит – это паук.&lt;br&gt;18. Джон Скит может выполнять парное программирование с самим собой.&lt;/p&gt;&lt;/blockquote&gt; &lt;p align="justify"&gt;&lt;a href="http://lh3.ggpht.com/-XWjI4RxyzCw/Tybwk_s18dI/AAAAAAAABZ0/zNhk0xfK_Fg/s1600-h/clip_image002%25255B6%25255D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="clip_image002" border="0" alt="clip_image002" src="http://lh5.ggpht.com/-70KCghMeT2M/Tybwlu8o78I/AAAAAAAABZ8/whPOOOBnv4w/clip_image002_thumb%25255B3%25255D.jpg?imgmax=800" width="453" height="364"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p align="justify"&gt;Большая часть фактов взята &lt;a href="http://meta.stackoverflow.com/questions/9134/jon-skeet-facts"&gt;отсюда&lt;/a&gt;, часть немного переделана, часть оставлена без изменений. Еще один факт, который не входит в перечень выше, но о котором стоит упомянуть, следующий. Джон, на самом деле, не является «профессиональным» .NET разработчиком, поскольку на работе (в Гугле) он пишет на Java, а язык C# является всего лишь его хобби;)    &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8596733192274108952-5957454856878752546?l=sergeyteplyakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sergeyteplyakov.blogspot.com/feeds/5957454856878752546/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8596733192274108952&amp;postID=5957454856878752546' title='Комментарии: 5'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/5957454856878752546'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/5957454856878752546'/><link rel='alternate' type='text/html' href='http://sergeyteplyakov.blogspot.com/2012/01/18.html' title='18 фактов о Джоне Ските'/><author><name>Sergey Teplyakov</name><uri>https://profiles.google.com/108967431947412296254</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-2weHUILo1ns/AAAAAAAAAAI/AAAAAAAABRo/POab-anD6xA/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/-70KCghMeT2M/Tybwlu8o78I/AAAAAAAABZ8/whPOOOBnv4w/s72-c/clip_image002_thumb%25255B3%25255D.jpg?imgmax=800' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8596733192274108952.post-932811142274077189</id><published>2012-01-18T23:42:00.001+02:00</published><updated>2012-01-19T08:18:02.573+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Анонсы'/><title type='text'>Первый workshop по .NET и архитектуре</title><content type='html'>&lt;p&gt;Завтра, 19 января мы в Учебном Центре попробуем провести первый workshop по .NET и архитектуре. Правда посвящен он будет не совсем обычной, казалось бы, для такого воркшопа теме, а именно &lt;a href="http://www.luxoft-training.ru/events/seminar/29496/"&gt;“Приемам функционального программирования на платформе .NET”&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Нет, никакого “батанства” там не предполагается, не будет никаких “моноидов в категории эндофункторов”, обсудим довольно мирские вещи, такие как неизменяемые типы данных, поговорим о вреде состояния и побочных эффектов, обсудим преимущества декларативности. Поговорим о &lt;a href="http://sergeyteplyakov.blogspot.com/2010/04/c.html"&gt;замыканиях&lt;/a&gt; и, возможно, успеем затронуть &lt;a href="http://sergeyteplyakov.blogspot.com/2011/07/blog-post.html"&gt;изменяемые значимые типы&lt;/a&gt;.&lt;/p&gt; &lt;a name='more'&gt;&lt;/a&gt; &lt;p&gt;&lt;a href="http://lh5.ggpht.com/-Y-F2O5ZZw4o/Txc8zW8Mu0I/AAAAAAAABZU/7m0ei9LZLkM/s1600-h/image%25255B11%25255D.png"&gt;&lt;img style="display: block; float: none; margin-left: auto; margin-right: auto" title="image" alt="image" src="http://lh3.ggpht.com/-svOH9oGcqM0/Txc80Gq7k-I/AAAAAAAABZY/tverACM56bI/image_thumb%25255B7%25255D.png?imgmax=800" width="417" height="338"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;Основное отличие воркшопа (или &lt;em&gt;воршопа&lt;/em&gt;, как выразился кто-то из наших девочек в одной из рассылок) заключается в том, что это будет скорее полуторачасовой обмен опытом, нежели формат преподаватель/студент. Это означает более живую дискуссию, нежели на семинаре, ну а что конкретно из этого получится – узнаем завтра.&lt;/p&gt; &lt;p&gt;Да, если кому интересно, то вход свободный. Место проведения: Киев, Радищева, 10/14, корпус Б. Четверг, 19 января, 17-30. Регистрироваться на это дело все равно нужно, и сделать это можно &lt;a href="http://www.luxoft-training.ru/events/seminar/29496/"&gt;здесь&lt;/a&gt;.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8596733192274108952-932811142274077189?l=sergeyteplyakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sergeyteplyakov.blogspot.com/feeds/932811142274077189/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8596733192274108952&amp;postID=932811142274077189' title='Комментарии: 3'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/932811142274077189'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/932811142274077189'/><link rel='alternate' type='text/html' href='http://sergeyteplyakov.blogspot.com/2012/01/workshop-net.html' title='Первый workshop по .NET и архитектуре'/><author><name>Sergey Teplyakov</name><uri>https://profiles.google.com/108967431947412296254</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-2weHUILo1ns/AAAAAAAAAAI/AAAAAAAABRo/POab-anD6xA/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/-svOH9oGcqM0/Txc80Gq7k-I/AAAAAAAABZY/tverACM56bI/s72-c/image_thumb%25255B7%25255D.png?imgmax=800' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8596733192274108952.post-5969949274884309761</id><published>2012-01-11T23:12:00.001+02:00</published><updated>2012-01-13T21:18:26.805+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Книги'/><category scheme='http://www.blogger.com/atom/ns#' term='Тестирование'/><title type='text'>The Art of Unit Testing</title><content type='html'>&lt;p align="justify"&gt;&lt;a href="http://lh6.ggpht.com/-kNdD42ay7cM/Tw37LTWbkLI/AAAAAAAABYA/BpdMZmemOM4/s1600-h/image%25255B10%25255D.png"&gt;&lt;img style="display: inline; float: left" title="image" alt="image" align="left" src="http://lh4.ggpht.com/-wnyesyyiDpo/Tw37MVQpe1I/AAAAAAAABYE/jiDd1QRVfn4/image_thumb%25255B8%25255D.png?imgmax=800" width="186" height="231"&gt;&lt;/a&gt;Есть некоторые категории знаний, которые профессиональный разработчик познает в процессе своей работы, не прилагая к этому особенных дополнительных усилий. Вот, например, мало кто из нас читал замечательную &lt;a href="http://www.ozon.ru/context/detail/id/4066500/"&gt;книгу по регулярным выражениям&lt;/a&gt; Джеффри Фирддла, чтобы познакомиться с одноименной темой. Безусловно, есть масса людей, для которых «регвыры» стали смыслом жизни и без подобных фундаментальных знаний никак не обойтись. Но в большинстве случаев пары мелких статей и справки в соответствующем разделе документации будет достаточно для более или менее комфортной работы с регулярными выражениями (если такое понятие, как «комфортная работа» с регулярными выражениями вообще существуетJ).  &lt;p align="justify"&gt;Аналогичным образом мы обычно относимся и к изучению юнит тестирования. Ведь юнит-тесты – это же не rocket science; для их изучения не требуется многолетняя подготовка и множество бессонных ночей проведенных за изучением толстенных «талмудов» от гуру юнит-тестирования. Концепцию автоматизированного тестирования кода можно объяснить за 10 минут, а познакомившись с одним из тестовых фреймворков семейства xUnit (еще 15 минут), вы сможете работать с любым другим фреймворком практически сразу же. Затем нужно будет потратить еще 20 минут на изучение какого-нибудь изоляционного фреймворка, типа Rhino Mocks, и, вуаля, у нас есть еще один профессионал в области юнит-тестов. &lt;/p&gt; &lt;a name='more'&gt;&lt;/a&gt; &lt;p align="justify"&gt;Подобный уровень владения инструментом, подкрепленный опытом, прагматизмом и здравым смыслом, достаточен для комфортной работы, поэтому может сложиться впечатление, что опытному разработчику пользы от этой книги не будет никакой. Я, честно говоря, был такого же мнения и вряд ли заинтересовался бы этой книгой, если бы мне не пришлось готовить доклад по юнит-тестированию для наших американских коллег. Ведь одно дело - это владение вопросом для собственного использования, другое дело – это внедрение чего-то нового и изменения мировоззрения команды разработчиков, большая часть из которой является твоими боссами.  &lt;p align="justify"&gt;Забегая вперед, стоит сказать, что я заблуждался и&lt;b&gt; &lt;/b&gt;книга &lt;a href="http://www.amazon.com/Art-Unit-Testing-Examples-Net/dp/1933988274/ref=sr_1_1?s=books&amp;amp;ie=UTF8&amp;amp;qid=1326315753&amp;amp;sr=1-1"&gt;The Art of Unit Testing&lt;/a&gt; by Roy Osherove будет полезна даже опытным разработчикам, но давайте обо всем по порядку.  &lt;p align="justify"&gt;Структура книги такова, что автор идет от наиболее простых и базовых понятий к более продвинутым вещам. Начинается все с простых примеров и определений, дается введение в TDD, рассказывается о «швах» приложения и об использовании «фейков», таких как &lt;a href="http://sergeyteplyakov.blogspot.com/2011/12/blog-post.html"&gt;моки и стабы&lt;/a&gt;, а также кратко описываются разные изоляционные фреймворки.  &lt;p align="justify"&gt;С самого начала Рой пытается вдолбить в голову читателя одну важную мысль: качественные тесты не менее важны, чем качественный production код! О том, что такое хорошие тесты, посвящена целая глава 7 The pillars of good tests, но даже за ее пределами тень «хороших» тестов будет преследовать читателя практически постоянно. О качестве кода написаны, наверное, десятки книг и бесчисленное множество статей; все мы знаем о том, как важно писать простой в сопровождении код и что делать для повышения его качества. Но к качеству тестов наше отношение зачастую не столь осмысленное, хотя плохие тесты могут испортить вам жизнь ничуть не хуже го$#о-кода в бизнес-логике.  &lt;p align="justify"&gt;&lt;i&gt;Нет никакого смысла в написании плохих юнит тестов, если только вы не новичок в этом деле и не учитесь таким образом писать хорошие тесты. Если вы по неосмотрительности собираетесь писать плохие тесты, то с тем же успехом вы можете вообще отказаться от этого занятия. Это убережет вас от неприятностей, связанных с отставанием от графика и избавит вас от дополнительных проблем с сопровождением. &lt;/i&gt; &lt;p align="justify"&gt;Очень здорово, что помимо принципов юнит тестирования Рой уделяет внимание и таким важным аспектам, как внедрение юнит тестирования в организации, тестирование унаследованного (legacy) кода и влиянию тестов на дизайн приложения. В вопросах дизайна Рой считает, что testable дизайн обусловлен, прежде всего, ограничениями языка или среды программирования и не существует в динамических языках программирования (поскольку там мы «замокать» можем все, что угодно). Я с таким подходом не согласен, поскольку считаю, что хороший дизайн по умолчанию хорошо тестируем, и что возможность написания юнит тестов является хорошим признаком простоты контрактов классов и признаком того, что класс не берет на себя слишком много.  &lt;p align="justify"&gt;&lt;font color="#526e66"&gt;&lt;b&gt;ПРИМЕЧАНИЕ&lt;/b&gt;&lt;br&gt;Подробнее об использовании юнит тестов как «лакмусовой бумажки» плохого дизайна можно почитать в заметке &lt;a href="http://sergeyteplyakov.blogspot.com/2011/11/blog-post_23.html"&gt;“Идеальная архитектура”&lt;/a&gt;&lt;/font&gt;&lt;font color="#526e66"&gt;&lt;/font&gt;&lt;font color="#526e66"&gt;.&lt;/font&gt;&lt;/p&gt; &lt;p align="justify"&gt;По признанию самого Роя он потратил на написание книги 3 года - и это больше, чем он потратил на «создание» двух своих детейJ и периодически это чувствуется. Так, например, в разных местах книги используются разный формат диаграмм классов, иногда отличается наименование тестовых классов и методов, да и вообще, ощущение дежавю периодически посещает. Есть и другие мелкие замечания. Так, автор на протяжении двух сотен страниц использует стандартный синтаксис утверждений библиотеки NUnit, а с 200-й страницы, вдруг начинает использовать Assert.That и заявляет о том, что этот синтаксис более декларативен. Очевидно, что года через полтора после начала работы над книгой Рой добрался до этого синтаксиса, который ему понравился, а сил и времени на изменение предыдущих примеров уже не было.  &lt;p align="justify"&gt;Единственным существенным замечанием к этой книге является слабое раскрытие темы параметризованных юнит тестов. Да, Рой упоминает вскользь об этой возможности, но уж очень поверхностно и где-то ближе к концу книги. Чувствуется некоторая несправедливость, когда о том, что плохо рассчитывать на порядок запуска юнит тестов автор тратит 4 (!) страницы, а на параметризованные тесты, которые могут сэкономить массу времени и существенно повысить читабельность тестов, тратится от силы 2 абзаца.  &lt;p align="justify"&gt;Можно найти и другие моменты, в которых ваше мнение будет отличаться от мнения автора, но в целом, книга “The Art of Unit Testing” является одним из лучших источников информации по теме юнит тестирования, которая может либо изменить ваше мировоззрение в правильную сторону, либо обобщить и структурировать уже существующие знания.  &lt;p align="justify"&gt;Оценка: 4+  &lt;h5 align="justify"&gt;&lt;b&gt;Дополнительные ссылки по теме&lt;/b&gt;&lt;/h5&gt; &lt;h6 align="justify"&gt;&lt;b&gt;Книги&lt;/b&gt;&lt;/h6&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://www.amazon.com/Art-Unit-Testing-Examples-Net/dp/1933988274"&gt;The Art of Unit Testing&lt;/a&gt; by Roy Osherove&lt;/div&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://www.amazon.com/xUnit-Test-Patterns-Refactoring-Code/dp/0131495054/ref=sr_1_1?s=books&amp;amp;ie=UTF8&amp;amp;qid=1326315311&amp;amp;sr=1-1"&gt;xUnit Test Patterns: Refactoring Test Code&lt;/a&gt; by Gerard Meszaros&lt;/div&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052/ref=sr_1_1?s=books&amp;amp;ie=UTF8&amp;amp;qid=1326315365&amp;amp;sr=1-1"&gt;Working Effectively with Legacy Code&lt;/a&gt; by Michael Feathers&lt;/div&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://www.amazon.com/Pragmatic-Unit-Testing-NUnit-2nd/dp/0977616673/ref=sr_1_2?s=books&amp;amp;ie=UTF8&amp;amp;qid=1326315408&amp;amp;sr=1-2"&gt;Pragmatic Unit Testing in C# with NUnit, 2&lt;sup&gt;nd&lt;/sup&gt; Edition&lt;/a&gt; by Andy Hunt and Dave Thomas&lt;/div&gt; &lt;ul&gt;&lt;/ul&gt; &lt;h6 align="justify"&gt;&lt;b&gt;Подкасты&lt;/b&gt;&lt;/h6&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://www.se-radio.net/2010/09/episode-167-the-history-of-junit-and-the-future-of-testing-with-kent-beck/"&gt;The History of JUnit and the Future of Testing with Kent Beck&lt;/a&gt; &lt;/div&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://itc.conversationsnetwork.org/shows/detail301.html"&gt;Kent Beck, Developer Testing&lt;/a&gt; &lt;/div&gt;&lt;b&gt;&lt;/b&gt; &lt;h6&gt;&lt;b&gt;Статьи&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/h6&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2011/12/blog-post.html"&gt;Стабы и моки&lt;/a&gt; &lt;/div&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2011/11/blog-post_23.html"&gt;Идеальная архитектура&lt;/a&gt; &lt;/div&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2010/06/first-principles.html"&gt;Пять принципов чистых тестов (F.I.R.S.T. Principles)&lt;/a&gt; &lt;/div&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2010/12/microsoft-moles.html"&gt;Microsoft Moles&lt;/a&gt;&lt;/div&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2011/03/blog-post.html"&gt;Частичные классы&lt;/a&gt;&lt;/div&gt; &lt;/li&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8596733192274108952-5969949274884309761?l=sergeyteplyakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sergeyteplyakov.blogspot.com/feeds/5969949274884309761/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8596733192274108952&amp;postID=5969949274884309761' title='Комментарии: 7'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/5969949274884309761'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/5969949274884309761'/><link rel='alternate' type='text/html' href='http://sergeyteplyakov.blogspot.com/2012/01/art-of-unit-testing.html' title='The Art of Unit Testing'/><author><name>Sergey Teplyakov</name><uri>https://profiles.google.com/108967431947412296254</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-2weHUILo1ns/AAAAAAAAAAI/AAAAAAAABRo/POab-anD6xA/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/-wnyesyyiDpo/Tw37MVQpe1I/AAAAAAAABYE/jiDd1QRVfn4/s72-c/image_thumb%25255B8%25255D.png?imgmax=800' height='72' width='72'/><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8596733192274108952.post-3949957894570913436</id><published>2012-01-03T09:39:00.001+02:00</published><updated>2012-01-03T09:39:38.748+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Философия программирования'/><title type='text'>Ретроспектива 2011</title><content type='html'>&lt;p align="justify"&gt;Мы живем с вами в удивительное время, ежедневно занимаемся любимым делом, за которое, к тому же, еще и неплохо платят. Количество «новых» технологий растет, как на дрожжах, поэтому сейчас, как никогда раньше, важно уметь отфильтровывать важное от несущественного и понимать, в какие направления следует «инвестировать» свое время. Ведь на самом деле, все эти новые технологии - не что иное, как хорошо забытые старые; зачастую это лишь проверенные временем идеи, которые воплощаются в жизнь в новой форме.  &lt;p align="justify"&gt;С другой стороны, очень важно не зацикливаться на «технологической» составляющей процесса разработки, ведь не менее важным фактором успешной работы вас как программиста является человеческий фактор и умение общаться с другими. Причем под этими «другими» я понимаю не только ваше начальство или ваших коллег, но и в большей степени близких вам людей, хорошие отношения с которыми делают вас лучше во всех отношениях. &lt;/p&gt; &lt;a name='more'&gt;&lt;/a&gt; &lt;p align="justify"&gt;Последним аспектом &lt;a href="http://sergeyteplyakov.blogspot.com/2011/11/blog-post_29.html"&gt;«хорошего программиста»&lt;/a&gt;, о котором хотелось бы сказать, является здоровье и хорошая физическая форма. Было очень забавно наблюдать за развитием событий в нашем офисе, когда перед открытием спортзала поднялся дикий ажиотаж: все считали, что как же так, зал небольшой, сотрудников много, толкотня, дескать, будет просто страшной. Ну вы же понимаете, чем это дело закончилось: зал практически пустует, и подавляющее большинство разговоров закончилось именно разговорами. Прогресс в спортивной форме доставляет не меньшее удовольствие, чем разработка кастомного WCF стека, борьба с утечками памяти или изучение нового языка программирования.  &lt;p align="justify"&gt;Самое интересное, что изначально это заметка начиналась совсем иначе, да и вообще, вместо желания пофилософствовать я хотел кратенько подвести итоги года и напомнить о тех интересных статьях, которые здесь появились, но потом, как говорится, «Остапа понесло» (с). Поскольку финальная цель все же не изменилась, так что теперь, с помощью нехитрого литературного приема под названием «перескакивание с одной темы на другую» давайте я постараюсь вернуться к исходной теме. &lt;/p&gt; &lt;p align="justify"&gt;Большинство моих статей можно разделить на 3 категории: (1) язык C# и платформа .NET; (2) архитектура, проектирование или философия программирования; (3) ревью или анонсы книг. Часть статей первой и второй категории оказались в журналах RSDN Magazine и Технология Клиент-Сервер, а часть из них попала на сайт SoftwarePeople.ru (не знаю почему, но эти ребята вызывают у меня нескрываемое уважение; может быть, потому что именно они привозили Тима Листера и Тома ДеМарко, не знаю), так что можно сделать вывод, что направление выбрано более или менее верное.  &lt;p align="justify"&gt;Итак, вот некоторые заметки, которые я бы хотел выделить за прошедший год (кстати, если у кого-то будет другое мнение по этому поводу – буду рад его услышать).  &lt;p align="justify"&gt;&lt;b&gt;По языку C#&amp;nbsp; и платформе .NET&lt;/b&gt;  &lt;ol&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2011/01/threadabort.html"&gt;О вреде метода Thread.Abort&lt;/a&gt;&lt;/div&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2011/02/c.html"&gt;Виртуальные события в языке C#&lt;/a&gt;&lt;/div&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2011/02/wcf.html"&gt;Что такое WCF?&lt;/a&gt;&lt;/div&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2011/03/tex.html"&gt;Визуализация деревьев выражений с помощью TeX&lt;/a&gt;&lt;/div&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2011/04/blog-post.html"&gt;Как не надо писать код&lt;/a&gt;&lt;/div&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2011/07/blog-post.html"&gt;О вреде изменяемых значимых типов&lt;/a&gt;&lt;/div&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2011/08/blog-post.html"&gt;О синглтонах и статических конструкторах&lt;/a&gt;&lt;/div&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2011/09/dispose-pattern.html"&gt;Dispose pattern&lt;/a&gt;&lt;/div&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2011/11/blog-post_03.html"&gt;Повторная генерация исключений&lt;/a&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt; &lt;p align="justify"&gt;&lt;b&gt;Философия и бла-бла-бла&lt;/b&gt;  &lt;ol&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2011/05/blog-post_26.html"&gt;Синдром рефакторинга&lt;/a&gt;&lt;/div&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2011/05/blog-post.html"&gt;Технический долг&lt;/a&gt;&lt;/div&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2011/06/blog-post.html"&gt;Эффект второй системы&lt;/a&gt;&lt;/div&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2011/09/blog-post_13.html"&gt;Принцип самурая&lt;/a&gt;&lt;/div&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2011/11/blog-post_23.html"&gt;Идеальная архитектура&lt;/a&gt;&lt;/div&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2011/11/blog-post_29.html"&gt;Кто такой хороший программист?&lt;/a&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt; &lt;p align="justify"&gt;&lt;b&gt;Книги&lt;/b&gt;  &lt;ol&gt; &lt;li&gt; &lt;div align="justify"&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2011/04/cnet.html"&gt;Классические книги по C#/.NET&lt;/a&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ol&gt; &lt;p align="justify"&gt;Ну и напоследок, всех читателей хочется поздравить с наступившим Новым годом и наступающим Рождеством, и пожелать, чтобы каждый из вас был, прежде всего, хорошим человеком, а уже потом – хорошим специалистом!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8596733192274108952-3949957894570913436?l=sergeyteplyakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sergeyteplyakov.blogspot.com/feeds/3949957894570913436/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8596733192274108952&amp;postID=3949957894570913436' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/3949957894570913436'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/3949957894570913436'/><link rel='alternate' type='text/html' href='http://sergeyteplyakov.blogspot.com/2012/01/2011.html' title='Ретроспектива 2011'/><author><name>Sergey Teplyakov</name><uri>https://profiles.google.com/108967431947412296254</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-2weHUILo1ns/AAAAAAAAAAI/AAAAAAAABRo/POab-anD6xA/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8596733192274108952.post-4391608760709193161</id><published>2011-12-19T06:33:00.001+02:00</published><updated>2011-12-22T15:19:04.703+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='Тестирование'/><title type='text'>Стабы и моки</title><content type='html'>&lt;p align="justify"&gt;Существует категория классов, которые тестировать весьма просто. Если класс зависит только от примитивных типов данных и не имеет никаких связей с другими бизнес-сущностями, то достаточно создать экземпляр этого класса, «пнуть» его некоторым образом путем изменения свойства или вызова метода и проверить ожидаемое состояние.  &lt;p align="justify"&gt;Это самый простой и эффективный способ тестирования, и любой толковый дизайн отталкивается от подобных классов, которые являются «строительными блоками» нижнего уровня, на основе которых затем уже строятся более сложные абстракции. Но количество классов, которые живут в такой «изоляции» не много по своей природе. Даже если мы по нормальному выделили всю логику по работе с базой данных (или сервисом) в отдельный класс (или набор классов), то рано или поздно появится кто-то, кто эти классы будет использовать для получения более высокоуровневого поведения и этого «кого-то» тоже нужно будет тестировать.  &lt;p align="justify"&gt;Но для начала давайте рассмотрим более типичный случай, когда логика по работе с базой данных или внешним сервисом, а также логика обработки этих данных сосредоточена в одном месте. &lt;/p&gt; &lt;a name='more'&gt;&lt;/a&gt;&lt;pre style="line-height: normal; font-family: ; background: white"&gt;&lt;font face="Consolas"&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Модель представления, предназначенная для управления входом &lt;/font&gt;&lt;/span&gt;
&lt;span&gt;&lt;font color="#008000"&gt;// пользователя в систему&lt;/font&gt;&lt;/span&gt;
&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;class&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;LoginViewModel&lt;/font&gt;&lt;/span&gt;
&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;{ &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; LoginViewModel() &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Читаем имя последнего пользователя&lt;/font&gt;&lt;/span&gt;
&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; UserName = ReadLastUserName(); &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;    // Имя пользователя; может быть изменено пользователем&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; UserName { &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;get&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;set&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;; } &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;    // Логиним пользователя UserName &lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;void&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; Login() &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Не обращаем внимание на дополнительную логику, которая должна быть &lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// выполнена. Считаем что нам достаточно просто сохранить имя текущего&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// пользователя&lt;/font&gt;&lt;/span&gt;
&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SaveLastUserName(UserName); &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;    // Читаем имя последнего залогиненного пользователя&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;private&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; ReadLastUserName() &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Не важно, как она на самом деле реализована ...&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Просто возвращаем что-нибудь, чтобы компилятор не возражал&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;return&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"Jonh Doe"&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;    // Сохраняем имя последнего пользователя&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;private&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;void&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; SaveLastUserName(&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; lastUserName) &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Опять таки, нам не интересно, как она реализована&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
}&lt;/font&gt;&lt;/font&gt;&lt;/pre&gt;
&lt;p align="justify"&gt;Когда речь заходит о тестировании подобных классов, то обычно эта вью-модель помещается на форму, которая затем тестируется руками Если вместо вью-модели подобное смешивание логики происходит при реализации серверных компонент, то они тестируются путем создания простого консольного приложения, которое будет вызывать необходимые высокоуровневые функции, тестируя, таким образом, весь модуль целиком. В обоих случаях такой вариант тестирования нельзя назвать очень уж автоматическим. 
&lt;p align="justify"&gt;&lt;font color="#526e66"&gt;&lt;b&gt;ПРИМЕЧАНИЕ&lt;/b&gt;&lt;br&gt;Не нужно бросать в меня камнями с криками «Да кто сегодня вообще такую хрень написать можно? Ведь уже столько всего написано о вреде такого подхода, да и вообще, у нас есть юнити-шмунити и другие полезности, так что это нереальный баян двадцатилетней давности!». Кстати, да, это баян, но, во-первых, речь не юнитях и других контейнерах, а о базовых принципах, а во-вторых, подобное «интеграционное» тестирование все еще невероятно популярно, во всяком случае, среди многих моих «зарубежных» коллег.&lt;/font&gt;&lt;/p&gt;
&lt;h5 align="justify"&gt;Создания «швов» для тестирования приложения&lt;/h5&gt;
&lt;p align="justify"&gt;Даже если не задумываться о том, какое количество новомодных принципов проектирования нарушает наша вью-модель, четко видно, что ее дизайн несколько … убог. Ведь даже если проектировать старым &lt;s&gt;дедовским&lt;/s&gt; бучевским методом, то становится понятно, что всю работу по сохранению имени последнего пользователя, логику по работе с базой данных (или другим внешним источником данных) нужно спрятать подальше с глаз долой и сделать это «проблемой» кого-то другого и использовать уже этого «кого-то» в качестве «строительного блока» для получения более высокоуровневого поведения:&lt;pre style="line-height: normal; font-family: ; background: white"&gt;&lt;font face="Consolas"&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;internal&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;class&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;LastUsernameProvider&lt;/font&gt;&lt;/span&gt;
&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;{ &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Читаем имя последнего пользователя из некоторого источника данных&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; ReadLastUserName() { &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;return&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"Jonh Doe"&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;; } &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Сохраняем это имя, откуда его можно будет впоследствии прочитать&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;void&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; SaveLastUserName(&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; userName) { }
}
 
&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;class&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;LoginViewModel&lt;/font&gt;&lt;/span&gt;
&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;{ &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Добавляем поле для получения и сохранения имени последнего пользователя&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;private&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;readonly&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;LastUsernameProvider&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; _provider = &lt;br&gt;                         &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;LastUsernameProvider&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;(); &lt;br&gt;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;    &lt;br&gt;    public&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; LoginViewModel() &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Теперь просто вызываем функцию нового вспомогательного класса&lt;/font&gt;&lt;/span&gt;
&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; UserName = _provider.ReadLastUserName(); &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;    public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; UserName { &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;get&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;set&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;; }&lt;br&gt;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;    &lt;br&gt;    public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;void&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; Login() &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Все действия по сохранению имени последнего пользователя также&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// делегируем новому классу&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _provider.SaveLastUserName(UserName); &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
}&lt;/font&gt;&lt;/font&gt;&lt;/pre&gt;
&lt;p align="justify"&gt;Пока что написание модульного теста все еще остается затруднительным, но становится понятным, как можно достаточно просто «подделать» реальную реализацию класса &lt;b&gt;LastUsernameProvider&lt;/b&gt; и сымитировать нужное для нас поведение. Достаточно выделить методы этого класса в отдельный интерфейс или просто сделать их виртуальными и переопределить в наследнике. После чего останется лишь «прикрутить» нужный нам объект в нашу вью-модель. 
&lt;p align="justify"&gt;&lt;font color="#526e66"&gt;&lt;b&gt;ПРИМЕЧАНИЕ&lt;/b&gt;&lt;br&gt;Честно говоря, я не большой фанат изменений в дизайне только ради «тестируемости» кода. Как показывает практика, нормальный ОО дизайн либо уже является достаточно «тестируемым» или же требует лишь минимальных телодвижений, чтобы сделать его таковым. Некоторые дополнительные мысли по этому поводу можно найти в заметке &lt;/font&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2011/11/blog-post_23.html"&gt;&lt;font color="#526e66"&gt;«Идеальная архитектура»&lt;/font&gt;&lt;/a&gt;&lt;font color="#526e66"&gt;.&lt;/font&gt;&lt;/p&gt;
&lt;p align="justify"&gt;Даже не прибегая ни к каким сторонним библиотекам для «инджекта» зависимостей мы можем сделать это самостоятельно несколько простыми способами. Нужную зависимость можно передать через дополнительный конструктор, через свойство или создать фабричный метод, который будет возвращать интерфейс &lt;b&gt;ILastUsernmameProvider&lt;/b&gt;. 
&lt;p align="justify"&gt;Давайте рассмотрим вариант с конструктором, который является довольно простым и популярным (при небольшом количестве внешних зависимостей он работает просто прекрасно).&lt;pre style="line-height: normal; font-family: ; background: white"&gt;&lt;font face="Consolas"&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Выделяем методы в интерфейс&lt;/font&gt;&lt;/span&gt;
&lt;span&gt;&lt;font color="#0000ff"&gt;internal&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;interface&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;ILastUsernameProvider&lt;/font&gt;&lt;/span&gt;
&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;{ &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; ReadLastUserName(); &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;void&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; SaveLastUserName(&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; userName);
}
 
&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;internal&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;class&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;LastUsernameProvider&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; : &lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;ILastUsernameProvider&lt;/font&gt;&lt;/span&gt;
&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;{ &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Читаем имя последнего пользователя из некоторого источника данных&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; ReadLastUserName() { &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;return&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"Jonh Doe"&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;; } &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Сохраняем это имя, откуда его можно будет впоследствии прочитать&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;void&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; SaveLastUserName(&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; userName) { }
}
 
&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;class&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;LoginViewModel&lt;/font&gt;&lt;/span&gt;
&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;{ &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;private&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;readonly&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;ILastUsernameProvider&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; _provider; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;    // Единственный открытый конструктор создает реальный провайдер&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; LoginViewModel() &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; : &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;this&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;LastUsernameProvider&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;()) &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {} &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;    // "Внутренний" предназначен только для тестирования и может принимать "фейк"&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;internal&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; LoginViewModel(&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;ILastUsernameProvider&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; provider) &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _provider = provider; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; UserName = _provider.ReadLastUserName(); &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;    public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; UserName { &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;get&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;set&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;; } &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;    public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;void&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; Login() &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; _provider.SaveLastUserName(UserName); &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
}&lt;/font&gt;&lt;/font&gt;&lt;/pre&gt;
&lt;p align="justify"&gt;Поскольку дополнительный конструктор является внутренним (internal), то он доступен только внутри этой сборке, а также «дружеской» сборке юнит-тестов. Конечно, если тестируемые классы являются внутренними, то проблемы не будет ни какой, но поскольку все «клиенты» внутреннего класса находятся в одной сборке, то и контролировать их проще. Подобный подход, основанный на добавлении внутреннего метода для установки «фальшивого» поведения является разумным компромиссом упрощения тестирования кода, не налагая ограничения на использования более сложных механизмов управления зависимостями, типа IoC контейнеров. 
&lt;p align="justify"&gt;&lt;font color="#526e66"&gt;&lt;b&gt;ПРИМЕЧАНИЕ&lt;/b&gt;&lt;br&gt;Одним из недостатков при работе с интерфейсами является падение читабельности, поскольку не понятно, сколько реализаций интерфейса существует и где находится реализация того или иного метода интерфейса. Такие инструменты, как Решарпер существенно смягчают эту проблему, поскольку поддерживают не только навигацию к объявлению метода (Go To Declaration), но также и навигацию к реализации метода (Go To Implementation):&lt;/font&gt;&lt;/p&gt;
&lt;p align="justify"&gt;&lt;a href="http://lh3.ggpht.com/-g2iNVXTEOvU/Tu6-pvUaRUI/AAAAAAAABXg/75ZXgc-MAWQ/s1600-h/clip_image002%25255B5%25255D.jpg"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto; padding-top: 0px" title="clip_image002" border="0" alt="clip_image002" src="http://lh4.ggpht.com/-vyv9VoRa41Y/Tu6-qns7LsI/AAAAAAAABXo/C_sCrmbawVg/clip_image002_thumb%25255B2%25255D.jpg?imgmax=800" width="372" height="155"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h5 align="justify"&gt;Проверка состояния vs проверка поведения&lt;/h5&gt;
&lt;p align="justify"&gt;Теперь давайте попробуем написать юнит-тест вначале для конструктора класса &lt;b&gt;LoginViewModel&lt;/b&gt;, который получает имя последнего залогиненного пользователя, а потом юнит-тест для метода &lt;b&gt;Login&lt;/b&gt;, после выполнения которого, имя последнего пользователя должно быть сохранено. 
&lt;p align="justify"&gt;Для нормальной реализации этих тестов нам нужна «фейковая» реализация интерфейса, при этом в первом случае, нам нужно вернуть произвольное имя последнего пользователя в методе &lt;b&gt;ReadLastUserName&lt;/b&gt;, а во втором случае – удостовериться, что вызван метод &lt;b&gt;SaveLastUserName&lt;/b&gt;. 
&lt;p align="justify"&gt;Именно в этом и отличаются два типа «фейковых» классов: &lt;b&gt;стабы предназначены для получения нужного состояния тестируемого объекта&lt;/b&gt;, а &lt;b&gt;моки применяются для проверки ожидаемого поведения тестируемого объекта&lt;/b&gt;. 
&lt;p align="justify"&gt;Стабы никогда не применяются в утверждениях, они простые «слуги», которые лишь моделируют внешнее окружение тестового класса; при этом в утверждениях проверяется состояние именно тестового класса, которое зависит от установленного состояния стаба.&lt;pre style="line-height: normal; font-family: ; background: white"&gt;&lt;font face="Consolas"&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Стаб возвращающее указанное имя последнего пользователя&lt;/font&gt;&lt;/span&gt;
&lt;span&gt;&lt;font color="#0000ff"&gt;internal&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;class&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;LastUsernameProviderStub&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; : &lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;ILastUsernameProvider&lt;/font&gt;&lt;/span&gt;
&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;{ &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Добавляем публичное поле, для простоты тестирования и &lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// возможности повторного использования этого класса&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; UserName; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;    // Реализация метода очень простая - просто возвращаем UserName&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; ReadLastUserName() &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;return&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; UserName; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;    // Этот метод в данном случае вообще не интересен&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;void&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; SaveLastUserName(&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; userName) { }
}
 
&lt;/font&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;[&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;TestFixture&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;]
&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;class&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;LoginViewModelTests&lt;/font&gt;&lt;/span&gt;
&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;{ &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Тестовый метод для проверки правильной реализации конструктора вью-модели&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; [&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;Test&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;] &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;void&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; TestViewModelConstructor() &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;var&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; stub = &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;LastUsernameProviderStub&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;(); &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;        // "моделируем" внешнее окружение&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; stub.UserName = &lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"Jon Skeet"&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Ух-ты!!&lt;br&gt;&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;var&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; vm = &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;LoginViewModel&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;(stub); &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;        // Проверяем состояние тестируемого класса&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;Assert&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;.That(vm.UserName, &lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;Is&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;.EqualTo(stub.UserName)); &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;}&lt;/font&gt;&lt;/font&gt;&lt;/pre&gt;
&lt;p align="justify"&gt;У моков же другая роль. Моки «подсовываются» тестируемому объекту, но не для того, чтобы создать требуемое окружение (хотя они могут выполнять и эту роль), а прежде всего для того, чтобы потом можно было проверить, что тестируемый объект &lt;b&gt;выполнил требуемые действия&lt;/b&gt;. (Именно поэтому такой вид тестирования называется &lt;b&gt;behavior&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;testing&lt;/b&gt;, в отличие от стабов, которые применяются для &lt;b&gt;state&lt;/b&gt;&lt;b&gt;-&lt;/b&gt;&lt;b&gt;based&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;testing&lt;/b&gt;).&lt;pre style="line-height: normal; font-family: ; background: white"&gt;&lt;font face="Consolas"&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Мок позволяет проверить, что метод SaveLastUserName был вызван &lt;/font&gt;&lt;/span&gt;
&lt;span&gt;&lt;font color="#008000"&gt;// с определенными параметрами&lt;/font&gt;&lt;/span&gt;
&lt;span&gt;&lt;font color="#0000ff"&gt;internal&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;class&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;LastUsernameProviderMock&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; : &lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;ILastUsernameProvider&lt;/font&gt;&lt;/span&gt;
&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;{ &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Теперь в этом поле будет сохранятся имя последнего сохраненного пользователя&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; SavedUserName; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;    // Нам все еще нужно вернуть правильное значение из этого метода,&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// так что наш "мок" также является и "стабом"&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; ReadLastUserName() { &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;return&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"Jonh Skeet"&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;;} &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;    // А вот в этом методе мы сохраним параметр в SavedUserName для &lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;void&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; SaveLastUserName(&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;string&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; userName) &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SavedUserName = userName; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
}
 
&lt;/font&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Проверяем, что при вызове метода Login будет сохранено имя последнего пользователя&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;[&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;Test&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;]
&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;public&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;void&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt; TestLogin()
{ &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;var&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; mock = &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;LastUsernameProviderMock&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;(); &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;var&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt; vm = &lt;/font&gt;&lt;span&gt;&lt;font color="#0000ff"&gt;new&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;&amp;nbsp;&lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;LoginViewModel&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;(mock); &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;    // Изменяем состояние вью-модели путем изменения ее свойства&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; vm.UserName = &lt;/font&gt;&lt;span&gt;&lt;font color="#a31515"&gt;"Bob Martin"&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// А затем вызываем метод Login&lt;/font&gt;&lt;/span&gt;
&lt;/font&gt;&lt;font face="Consolas"&gt;&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; vm.Login(); &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#008000"&gt;// Теперь мы проверяем, что был вызван метод SaveLastUserName&lt;/font&gt;&lt;/span&gt;
&lt;font color="#000000"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;Assert&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;.That(mock.SavedUserName, &lt;/font&gt;&lt;span&gt;&lt;font color="#2b91af"&gt;Is&lt;/font&gt;&lt;/span&gt;&lt;font color="#000000"&gt;.EqualTo(vm.UserName));
}&lt;/font&gt;&lt;/font&gt;&lt;/pre&gt;
&lt;h5 align="justify"&gt;А зачем мне знать об этих отличиях?&lt;/h5&gt;
&lt;p align="justify"&gt;Действительно, разница в понятиях может показаться незначительной, особенно если вы реализуете подобные «фейки» руками. В этом случае знание этих паттернов лишь позволит говорить с другими разработчиками на одном языке и упростит наименование фейковых классов. 
&lt;p align="justify"&gt;Однако рано или поздно вам может надоесть это чудесное занятие по ручной реализации интерфейсов и вы обратите свое внимание на один из Isolation фреймворков, таких как Rhino Mocks, Moq или Microsoft Moles. Там эти термины встретятся обязательно и понимание отличий между этими типами фейков вам очень пригодится. 
&lt;p align="justify"&gt;Я осознанно не касался ни одного из этих фреймворков, поскольку каждый из них заслуживает отдельной статьи и ИМО лишь усложнит понимание этих понятий. Но если вам все же интересно посмотреть на некоторые из этих фреймворков более подробно, то об одном из них я писал более подробно: &lt;a href="http://sergeyteplyakov.blogspot.com/2010/12/microsoft-moles.html"&gt;“Microsoft Moles”&lt;/a&gt;. 
  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8596733192274108952-4391608760709193161?l=sergeyteplyakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sergeyteplyakov.blogspot.com/feeds/4391608760709193161/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8596733192274108952&amp;postID=4391608760709193161' title='Комментарии: 9'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/4391608760709193161'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/4391608760709193161'/><link rel='alternate' type='text/html' href='http://sergeyteplyakov.blogspot.com/2011/12/blog-post.html' title='Стабы и моки'/><author><name>Sergey Teplyakov</name><uri>https://profiles.google.com/108967431947412296254</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-2weHUILo1ns/AAAAAAAAAAI/AAAAAAAABRo/POab-anD6xA/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/-vyv9VoRa41Y/Tu6-qns7LsI/AAAAAAAABXo/C_sCrmbawVg/s72-c/clip_image002_thumb%25255B2%25255D.jpg?imgmax=800' height='72' width='72'/><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8596733192274108952.post-3845026596237252943</id><published>2011-11-29T03:40:00.001+02:00</published><updated>2011-11-29T03:40:18.476+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Философия программирования'/><title type='text'>Кто такой “хороший программист”?</title><content type='html'>&lt;p align="justify"&gt;Я уже не первый раз замечаю, что сам не знаю, чем закончится начатая мною статья. Вот, например, совсем недавно меня попросили написать небольшую заметку в корпоративную газету о .NET направлении. Я выбрал направление, начал его развивать, в результате чего получилась статья, в которой .NET-а не оказалось вовсе. Так что в этот раз мы продолжим философскую тему, но если в прошлый раз речь шла о &lt;a href="http://sergeyteplyakov.blogspot.com/2011/11/blog-post_23.html"&gt;хорошей архитектуре&lt;/a&gt;, то на этот раз речь пойдет о том, кто такой хороший программист и какие вопросы стоит задавать себе время от времени, чтобы двигаться в правильном направлении.  &lt;p align="justify"&gt;Что отличает хорошего программиста от посредственного? И как самому стать хорошим программистом и заслужить среди друзей и коллег «почет и уважение»? &lt;/p&gt; &lt;a name='more'&gt;&lt;/a&gt; &lt;p align="justify"&gt;Честно говоря, не знаю. Зачастую даже сложно сказать, что понимается под этой фразой «хороший программист». С одной стороны, мы привыкли думать, что «хороший» программист – это обязательно гик в очках и растянутом свитере, победитель олимпиады по программированию, который в уме может умножать шестизначные цифры и читать машинный код прямо в двоичном формате. С другой стороны, на собеседованиях мы тоже пытаемся найти «хороших» программистов и там у нас есть другие критерии отбора: поскольку победителей олимпиад не так и много, а вакансий «ведущих» и «главных» пруд пруди, то эта планка опускается до более приземленного уровня. Так на собеседованиях мы задаем (или нам задают) вопросы попроще: что-нибудь о конкретных языках или технологиях, в результате чего проверяется скорее память кандидата, а не его умение думать головой.  &lt;p align="justify"&gt;&lt;i&gt;Думать головой&lt;/i&gt;? Ну что ж, это очень даже неплохая отличительная черта хорошего программиста. Как ни крути, но именно серое (или какое оно там) вещество, расположенное в нашей голове, привело нас в эту профессию; и именно «думанием» профессиональный программист зарабатывает себе на жизнь, занимаясь этим большую часть своего времени. А раз так, то почему бы не &lt;i&gt;подумать&lt;/i&gt; о том, как стать, если не хорошим программистом в общепринятом понимании, то хотя бы немного лучше по сравнению с тем, кем был вчера.  &lt;p align="justify"&gt;Многие из нас после очередной итерации в разработке своего любимого проекта проводят ретроспективы. Довольно часто ретроспективы – это унылое собрание, где поднимается минимум острых вопросов и еще меньше из них затем решается. Но иногда они заставляют некоторых членов команды по-новому взглянуть на решаемые задачи и что-либо изменить в ежедневной работе. В общем, это работает. Не всегда, но работает. А раз это работает, то почему бы не попробовать применить этот же самый подход, но не только для улучшения процесса разработки, а для улучшения себя, как программиста? Почему бы не задать несколько вопросов самому себе, ответы на которые помогли бы понять, в каком направлении двигаемся мы сами?  &lt;p align="justify"&gt;Вот, например, как часто вы задаете себе подобные вопросы: «Чем отличается ваш код сегодня от вашего кода полугодичной давности? Становится ли он лучше или, быть может, он уже давно достиг совершенства? Как изменились ваши взгляды на разработку ПО за это время? Что нового вы узнали из этой безграничной области? Когда в последний раз вы написали что-либо ради интереса, а не ради «производственной необходимости»? Когда в последний раз вы помогли кому-то с техническим вопросом?»  &lt;p align="justify"&gt;Конечно, ответы на эти вопросы не скажут, хороший вы программист или нет. Смысл их в том, чтобы понять, становитесь вы «лучше» или топчетесь на месте. Хороший программист – это не гениальный подросток, а простой толковый парень или девушка, любящие свое дело. Человек, в котором сочетается прагматизм и стремление к совершенству, любовь к новому и глубокие знания настоящего, стремление к обучению и желание учить кого-то еще. Большинство хороших программистов прекрасно понимают, что они знают очень мало и находят в себе желание и стремление это исправлять. У каждого из нас свой путь к получению новых знаний и здесь, как и в программировании, тоже не стоит искать «серебряную пулю».  &lt;p align="justify"&gt;Так что, учите, читайте, узнавайте что-то новое, пишите и рассказывайте об этом. И вообще, не думайте о том, хороший вы программист или нет, глупости все это. Ведь самое интересное в этом деле – это путь, а не результат!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8596733192274108952-3845026596237252943?l=sergeyteplyakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sergeyteplyakov.blogspot.com/feeds/3845026596237252943/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8596733192274108952&amp;postID=3845026596237252943' title='Комментарии: 9'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/3845026596237252943'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/3845026596237252943'/><link rel='alternate' type='text/html' href='http://sergeyteplyakov.blogspot.com/2011/11/blog-post_29.html' title='Кто такой “хороший программист”?'/><author><name>Sergey Teplyakov</name><uri>https://profiles.google.com/108967431947412296254</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-2weHUILo1ns/AAAAAAAAAAI/AAAAAAAABRo/POab-anD6xA/s512-c/photo.jpg'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8596733192274108952.post-190195344034840662</id><published>2011-11-23T22:51:00.001+02:00</published><updated>2011-11-24T08:22:10.280+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Принципы разработки'/><category scheme='http://www.blogger.com/atom/ns#' term='Философия программирования'/><title type='text'>Идеальная архитектура</title><content type='html'>&lt;p align="justify"&gt;Существует много разных взглядов на разработку архитектуры и дизайна современных приложений. Некоторые архитекторы стремятся продумать все до мелочей, разрисовать use case-ы всех классов и модулей, проанализировать миллион возможных способов их использования, все их обязательно задокументировать и уже потом приступить к этапу кодирования.  &lt;p align="justify"&gt;Другие, наоборот, считают, что «думать уже поздно» и давным-давно пора «делать», поэтому они кидаются на баррикады с криками «Ура», выдавая на гора тонны никому не нужного кода. Как и любая крайность, такой подход не приводит ни к чему хорошему. Но, как и во многих других случаях, существует промежуточный вариант, когда проектированию и архитектуре уделяется должное внимание, когда они не ставятся во главу угла, а используются для выявления правильных абстракций и поиска компромиссов в противоречивых требованиях заказчика. &lt;/p&gt; &lt;a name='more'&gt;&lt;/a&gt; &lt;p align="justify"&gt;Когда речь заходит о дизайне классов, архитектуре модулей или об ответственности различных слоев приложения, то решающую роль при их проектировании играют понятия &lt;i&gt;сцепления &lt;/i&gt;(cohesion) и &lt;i&gt;связанности&lt;/i&gt; (coupling). Еще Кристофер Александер, «папа» шаблонов проектирования, писал о том, что основной задачей при декомпозиции системы является осуществление двух условий: (1) максимизация связей внутри компонентов (&lt;i&gt;высокое внутреннее сцепление&lt;/i&gt;, tight internal cohesion) и (2) минимизация связей между компонентами (&lt;i&gt;низкая внешняя связанность&lt;/i&gt;, loose external coupling).  &lt;p align="justify"&gt;&lt;font color="#526e66"&gt;&lt;b&gt;ПРИМЕЧАНИЕ&lt;/b&gt;&lt;br&gt;Подробнее об истории возникновения такого уникального понятия, как шаблоны проектирования (design patterns) можно прочитать в статье: &lt;/font&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2010/01/blog-post.html"&gt;&lt;font color="#526e66"&gt;Шаблоны проектирования. История успеха&lt;/font&gt;&lt;/a&gt;&lt;font color="#526e66"&gt;.&lt;/font&gt;&lt;/p&gt; &lt;p align="justify"&gt;Благодаря Бобу Мартину и некоторым другим известным личностям, в нашем арсенале появились принципы, такие как S.O.L.I.D., которые позволяют определить более точно (или, быть может, более формально), отвечает ли дизайн системы приведенным выше основополагающим принципам или нет. Я же обычно, прежде чем переходить к «тяжелой артиллерии», в виде подобных принципов использую более простой подход. Я задаю себе следующий вопрос: «а насколько реально покрыть основную функциональность этого класса модульными тестами?» Если ответ положительный, то наверняка указанный класс обладает достаточно &lt;i&gt;высоким сцеплением&lt;/i&gt; и &lt;i&gt;низкой связанностью&lt;/i&gt; с другими классами. Если даже чисто теоретическое написание юнит-теста невозможно, поскольку ответственность класса непонятна, он содержит кучу несвязанных друг с другом полей и методов, и зависит от двух десятков других сущностей, тогда с дизайном явно что-то не так.  &lt;p align="justify"&gt;&lt;a href="http://lh6.ggpht.com/-k5K5Ejhnv9c/Ts1c4okisZI/AAAAAAAABXI/Tn8K_jdlT4M/s1600-h/clip_image002%25255B6%25255D.png"&gt;&lt;img style="display: block; float: none; margin-left: auto; margin-right: auto" title="clip_image002" alt="clip_image002" src="http://lh5.ggpht.com/-Al5oSGbY72o/Ts1c5VMKWMI/AAAAAAAABXM/HvloMEYpF-M/clip_image002_thumb%25255B3%25255D.png?imgmax=800" width="502" height="266"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p align="center"&gt;Рисунок 1 – И как это чудо тестировать? А ведь в реальной жизни связей может быть в пару раз больше  &lt;p align="justify"&gt;&lt;font color="#526e66"&gt;&lt;b&gt;СОВЕТ&lt;/b&gt;&lt;br&gt;Используйте принцип «тестируемости» класса в качестве «лакмусовой бумажки» хорошего дизайна класса. Даже если вы не напишите ни строчки тестового кода (хотя зря!), ответ на этот вопрос в 90% случаев поможет понять, насколько все «хорошо» или «плохо» с его дизайном.&lt;/font&gt;&lt;/p&gt; &lt;p align="justify"&gt;Когда речь заходит об архитектуре приложения или модуля, то «тестируемость» не может быть критерием качества. Конечно же, мы можем создать интеграционные тесты целых подсистем, но они, скорее всего, дадут совсем немного полезной информации об адекватности их архитектуры.  &lt;p align="justify"&gt;При решении архитектурных вопросов, таких как выбор платформы построения распределенных приложений, выбор UI-фреймворка или архитектуры слоя доступа данных, разумно задавать себе другой вопрос: «А что будет, если принятое сейчас решение будет ошибочным?» или «А вообще, должен ли я принять окончательное решение прямо сейчас?».  &lt;p align="justify"&gt;&lt;i&gt;Абстракция&lt;/i&gt; и &lt;i&gt;инкапсуляция&lt;/i&gt;, все еще являются нашими лучшими друзьями и вот как раз они прекрасно применимы, как к отдельным классам, так и целым модулям или слоям приложения. Использования WCF должно быть максимально спрятано в коммуникационном слое, UI фреймворк не должен торчать из всех базовых классов, а архитектура слоя доступа к данным не должна налагать ограничения на бизнес-классы приложения. Конечно, согласно “&lt;a href="http://sergeyteplyakov.blogspot.com/2011/10/blog-post_26.html"&gt;закону дырявых абстракций&lt;/a&gt;”, периодически ненужные подробности все же будут пробираться в другие слои приложения, но нам нужно хотя бы постараться их ограничить.  &lt;p align="justify"&gt;Хорошая архитектура не должна быть продумана до мелочей; хорошая архитектура должна быть продумана достаточно хорошо, чтобы понять, насколько сильно архитектурные ошибки в одной части приложения поломают совсем другие логически не связанные модули.  &lt;p align="justify"&gt;&lt;font color="#526e66"&gt;&lt;strong&gt;СОВЕТ&lt;br&gt;&lt;/strong&gt;Если «лакмусовой бумажкой» качества дизайна классов была их тестируемость, то лакмусовой бумажкой качества архитектуры можно считать ее «гибкость». Спросите у себя: «А что будет, если текущее архитектурное решение окажется неверным?», «Какое количество модулей подвергнется при этом изменениям?». По возможности, архитектурные решения не должны «вырубаться в камне», и последствия архитектурных ошибок должны быть в разумной степени ограничены.&lt;/font&gt;&lt;/p&gt; &lt;h4 align="justify"&gt;Отступление от темы. Clean Architecture от Боба Мартина&lt;/h4&gt; &lt;p align="justify"&gt;О важности прагматичного взгляда на архитектуру написано довольно много. Гради Буч в своей знаменитой книге неоднократно останавливается на важности абстрагирования и инкапсуляции на самых разных уровнях. В замечательной книге «97 этюдов для архитекторов программных систем» многие авторы не раз говорят о вреде «вырубания решений в камне» и преимуществах простоты перед гибкостью в вопросах архитектуры.  &lt;p align="justify"&gt;Одним из последних известных авторов, тему архитектуры поднял Боб Мартин. В одной из своих презентаций, а также в одном из своих постов об архитектуре Боб написал следующее: … &lt;i&gt;Хорошая архитектура позволяет ОТКЛАДЫВАТЬ принятие ключевых решений… Хорошая архитектура максимизирует количество НЕ ПРИНЯТЫХ решений&lt;/i&gt;.  &lt;p align="justify"&gt;Я, конечно, не на все 100% согласен со стариной Бобом о том, что можно отложить &lt;i&gt;все&lt;/i&gt; архитектурные решения, но согласен с тем, что хорошая архитектура &lt;i&gt;позволяет&lt;/i&gt; отложить &lt;i&gt;многие&lt;/i&gt; из них. И здесь даже дело не только и не столько во времени принятия этих решений, а в стоимости их последующих исправлений.  &lt;p align="justify"&gt;&lt;font color="#526e66"&gt;&lt;b&gt;ПРИМЕЧАНИЕ&lt;/b&gt;&lt;br&gt;Презентацию Боба Мартина, о которой я упоминал выше, можно найти &lt;/font&gt;&lt;a href="http://dl.dropbox.com/u/4730299/Architecture%20the%20lost%20years.pdf"&gt;здесь&lt;/a&gt;&lt;font color="#526e66"&gt;&lt;/font&gt;&lt;font color="#526e66"&gt;, небольшое видео, с обсуждением этого вопроса, &lt;/font&gt;&lt;a href="http://cleancoder.posterous.com/architecture-deference"&gt;здесь&lt;/a&gt;&lt;font color="#526e66"&gt;&lt;/font&gt;&lt;font color="#526e66"&gt;. И вот несколько постов Боба на эту же тему: &lt;/font&gt;&lt;a href="http://blog.8thlight.com/uncle-bob/2011/11/22/Clean-Architecture.html"&gt;Clean Architecture&lt;/a&gt;&lt;font color="#526e66"&gt;&lt;/font&gt;&lt;font color="#526e66"&gt; и &lt;/font&gt;&lt;a href="http://blog.8thlight.com/uncle-bob/2011/09/30/Screaming-Architecture.html"&gt;Screaming Architecture&lt;/a&gt;&lt;font color="#526e66"&gt;&lt;/font&gt;&lt;font color="#526e66"&gt;.&lt;/font&gt;&lt;/p&gt; &lt;h4 align="justify"&gt;Заключение&lt;/h4&gt; &lt;p align="justify"&gt;Я очень советую читателям не воспринимать приведенные выше принципы как «серебряную пулю», которая гарантированно даст вам ответ на вопрос о том, как получить «идеальный дизайн» или «идеальную архитектуру». Подобные вопросы могут лишь показать пробелы, но не могут показать правильный путь, они способны лишь зажечь тревожный огонек в вашей голове и побудить вас к более тщательному анализу дизайна класса или принимаемых архитектурных решений.    &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8596733192274108952-190195344034840662?l=sergeyteplyakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sergeyteplyakov.blogspot.com/feeds/190195344034840662/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8596733192274108952&amp;postID=190195344034840662' title='Комментарии: 5'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/190195344034840662'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/190195344034840662'/><link rel='alternate' type='text/html' href='http://sergeyteplyakov.blogspot.com/2011/11/blog-post_23.html' title='Идеальная архитектура'/><author><name>Sergey Teplyakov</name><uri>https://profiles.google.com/108967431947412296254</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-2weHUILo1ns/AAAAAAAAAAI/AAAAAAAABRo/POab-anD6xA/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/-Al5oSGbY72o/Ts1c5VMKWMI/AAAAAAAABXM/HvloMEYpF-M/s72-c/clip_image002_thumb%25255B3%25255D.png?imgmax=800' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8596733192274108952.post-7382891056291620728</id><published>2011-11-09T23:45:00.001+02:00</published><updated>2011-11-09T23:48:39.198+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Reactive Extensions'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Observable.Generate и перечисление списков</title><content type='html'>&lt;p align="justify"&gt;В библиотеке реактивных расширений (a.k.a. Rx – Reactive Extensions) существует вспомогательный метод &lt;b&gt;Observable&lt;/b&gt;&lt;b&gt;.&lt;/b&gt;&lt;b&gt;Generate&lt;/b&gt;, который позволяет генерировать простые observable-последовательности.&lt;pre style="font-family: consolas; background: white; color: black; font-size: 13px"&gt;&lt;span style="color: #2b91af"&gt;IObservable&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;string&lt;/span&gt;&amp;gt; xs = &lt;span style="color: #2b91af"&gt;Observable&lt;/span&gt;.Generate&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;, &lt;span style="color: blue"&gt;string&lt;/span&gt;&amp;gt;(&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;     initialState: 0, &lt;span style="color: green"&gt;// начальное значение&lt;br&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; condition: x =&amp;gt; x &amp;lt; 10, &lt;span style="color: green"&gt;// условие завершения генерации&lt;br&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; iterate: x =&amp;gt; x + 1, &lt;span style="color: green"&gt;// изменение значения&lt;br&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; resultSelector: x =&amp;gt; x.ToString() &lt;span style="color: green"&gt;// преобразование текущего значения в результат&lt;br&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; );
 
xs.Subscribe(x =&amp;gt; &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515"&gt;"x: &lt;/span&gt;&lt;span style="color: mediumseagreen"&gt;{0}&lt;/span&gt;&lt;span style="color: #a31515"&gt;"&lt;/span&gt;, x));
&lt;/pre&gt;&lt;a name='more'&gt;&lt;/a&gt;
&lt;p align="justify"&gt;&lt;font color="#526e66"&gt;&lt;b&gt;ПРИМЕЧАНИЕ&lt;br&gt;&lt;/b&gt;Обратите внимание на явное указание параметров обобщенного метода. Причина такого странного поведения (ведь обобщенные параметры методов должны спокойно выводиться компилятором) в том, что в компиляторе языка C# есть известный баг, в результате которого, вывод типов плохо дружит с именованными параметрами. Подробнее об этом можно почитать на &lt;/font&gt;&lt;a href="http://stackoverflow.com/questions/6542822/named-arguments-and-generic-type-inference-in-c-sharp-4-0"&gt;stackoverflow&lt;/a&gt;&lt;font color="#526e66"&gt;&lt;/font&gt;&lt;font color="#526e66"&gt; или на &lt;/font&gt;&lt;a href="http://connect.microsoft.com/VisualStudio/feedback/details/678498"&gt;connect-e&lt;/a&gt;&lt;font color="#526e66"&gt;&lt;/font&gt;&lt;font color="#526e66"&gt;. Да, кстати, это дело пофиксили в Visual Studio 11.&lt;/font&gt;&lt;/p&gt;
&lt;p align="justify"&gt;Метод &lt;b&gt;Generate&lt;/b&gt; напоминает простой цикл &lt;b&gt;for&lt;/b&gt;, который состоит из тех же самых этапов: инициализации начального значения, условия выхода из цикла и изменения переменной цикла. И если мы захотим сгенерировать обычную последовательность в памяти, то мы будем использовать именно цикл &lt;b&gt;for&lt;/b&gt; внутри блока итераторов:&lt;pre style="font-family: consolas; background: white; color: black; font-size: 13px"&gt;&lt;span style="color: blue"&gt;static&lt;/span&gt;&amp;nbsp;&lt;span style="color: #2b91af"&gt;IEnumerable&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;string&lt;/span&gt;&amp;gt; GenerateSequence()
{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;for&lt;/span&gt; (&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;int&lt;/span&gt; x = 0; &lt;span style="color: green"&gt;//initialState&lt;br&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; x &amp;lt; 10;&amp;nbsp; &lt;span style="color: green"&gt;// condition&lt;br&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; x = x + 1&amp;nbsp; &lt;span style="color: green"&gt;// iterate&lt;br&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; )&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;yield&lt;/span&gt;&amp;nbsp;&lt;span style="color: blue"&gt;return&lt;/span&gt; x.ToString(); &lt;span style="color: green"&gt;// resultSelector&lt;br&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
}
 
&lt;span style="color: blue"&gt;static&lt;/span&gt;&amp;nbsp;&lt;span style="color: blue"&gt;void&lt;/span&gt; Main(&lt;span style="color: blue"&gt;string&lt;/span&gt;[] args)
{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;var&lt;/span&gt; xs = GenerateSequence();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;foreach&lt;/span&gt; (&lt;span style="color: blue"&gt;var&lt;/span&gt; x &lt;span style="color: blue"&gt;in&lt;/span&gt; xs)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515"&gt;"x: &lt;/span&gt;&lt;span style="color: mediumseagreen"&gt;{0}&lt;/span&gt;&lt;span style="color: #a31515"&gt;"&lt;/span&gt;, x);
}&lt;/pre&gt;
&lt;p align="justify"&gt;Оба варианта кода эквивалентны, но если в первом случае создается &lt;b&gt;push&lt;/b&gt;-последовательность (в виде интерфейса &lt;b&gt;IObservable&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;of&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;T&lt;/b&gt;), которая будет самостоятельно уведомлять о поступлении новых элементов, то во втором случае мы получаем &lt;b&gt;pull&lt;/b&gt;-последовательность (в виде интерфейса &lt;b&gt;IEnumerable&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;of&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;T&lt;/b&gt;), из которой нужно «плоскогубцами» эти элементы вытаскивать. 
&lt;p align="justify"&gt;&lt;font color="#526e66"&gt;&lt;b&gt;ПРИМЕЧАНИЕ&lt;/b&gt;&lt;br&gt;Если очень коротко, то &lt;b&gt;pull&lt;/b&gt;-модель – это такая модель взаимодействия приложения с окружающим миром, когда ведущую роль выполняет именно приложение. Если посмотреть на интерфейс &lt;b&gt;IEnumerable&lt;/b&gt;, то именно вызывающий код «управляет» потоком элементом, вызывая метод &lt;b&gt;MoveNext&lt;/b&gt; для перехода к следующему. В &lt;b&gt;push&lt;/b&gt;-модели действия разворачиваются по-другому: внешнее окружение само уведомляет приложение о некоторых событиях (например, о наличии новых данных), а приложение лишь «реагирует» на них. Подробнее об этих моделях, о дуализме интерфейсов &lt;b&gt;IEnumerable&lt;/b&gt;/&lt;b&gt;IObservable&lt;/b&gt;, а также о других возможностях библиотеки реактивных расширений можно почитать в статье: &lt;/font&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2010/11/blog-post.html"&gt;Реактивные расширения и асинхронные операции&lt;/a&gt;&lt;font color="#526e66"&gt;&lt;/font&gt;&lt;font color="#526e66"&gt;.&lt;/font&gt;&lt;/p&gt;
&lt;p align="justify"&gt;Поскольку интерфейсы push и pull-последовательностей (&lt;b&gt;IObservable&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;of&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;T&lt;/b&gt; и &lt;b&gt;IEnumerable&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;of&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;T&lt;/b&gt;, соответственно) являются такими похожими, то к нам в голову легко может затесаться мысль о преобразовании из одной формы последовательности в другую:&lt;pre style="font-family: consolas; background: white; color: black; font-size: 13px"&gt;&lt;span style="color: blue"&gt;int&lt;/span&gt;[] ai = &lt;span style="color: blue"&gt;new&lt;/span&gt;[] { 1, 2, 3 };
 
&lt;span style="color: #2b91af"&gt;IObservable&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; oi = &lt;span style="color: #2b91af"&gt;Observable&lt;/span&gt;.Generate(&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green"&gt;/*initialState:*/&lt;/span&gt; ai.GetEnumerator(),&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green"&gt;/*condition:*/&lt;/span&gt; e =&amp;gt; e.MoveNext(),&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green"&gt;/*iterate:*/&lt;/span&gt; e =&amp;gt; e,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green"&gt;/*resultSelector:*/&lt;/span&gt; e =&amp;gt; (&lt;span style="color: blue"&gt;int&lt;/span&gt;)e.Current&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; );
 
oi.Subscribe(i =&amp;gt; &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515"&gt;"i: &lt;/span&gt;&lt;span style="color: mediumseagreen"&gt;{0}&lt;/span&gt;&lt;span style="color: #a31515"&gt;"&lt;/span&gt;, i));
&lt;/pre&gt;
&lt;p align="justify"&gt;В целом ничего криминального, и при выполнении этого кода мы ожидаемо получим: 
&lt;p&gt;1&lt;br&gt;2&lt;br&gt;3&lt;/p&gt;
&lt;p align="justify"&gt;Но что, если вместо массива нам захочется сделать тоже самое со списком:&lt;pre style="font-family: consolas; background: white; color: black; font-size: 13px"&gt;&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; li = &lt;span style="color: blue"&gt;new&lt;/span&gt;&amp;nbsp;&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; {1, 2, 3};&lt;br&gt;&lt;span style="color: #2b91af"&gt;IObservable&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; oi = &lt;span style="color: #2b91af"&gt;Observable&lt;/span&gt;.Generate(&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green"&gt;/*initialState:*/&lt;/span&gt; li.GetEnumerator(),&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green"&gt;/*condition:*/&lt;/span&gt; e =&amp;gt; e.MoveNext(),&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green"&gt;/*iterate:*/&lt;/span&gt; e =&amp;gt; e,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green"&gt;/*resultSelector:*/&lt;/span&gt; e =&amp;gt; e.Current&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; );
 
oi.Subscribe(i =&amp;gt; &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515"&gt;"i: &lt;/span&gt;&lt;span style="color: mediumseagreen"&gt;{0}&lt;/span&gt;&lt;span style="color: #a31515"&gt;"&lt;/span&gt;, i));&lt;/pre&gt;
&lt;p&gt;И, совершенно естественно, что в этом случае мы получим … бесконечную последовательность нулей. 
&lt;p align="justify"&gt;&lt;font color="#526e66"&gt;&lt;b&gt;ПРИМЕЧАНИЕ&lt;/b&gt;&lt;br&gt;Да, я знаю, что самым простым способом преобразования простой последовательности в &lt;b&gt;observable&lt;/b&gt;-последовательность, является использование метода расширения &lt;b&gt;Observable&lt;/b&gt;&lt;b&gt;.&lt;/b&gt;&lt;b&gt;ToObservable&lt;/b&gt;, который «расширяет» интерфейс &lt;b&gt;IEnumerable&lt;/b&gt;. Но, предположим, что мы либо о нем не знаем, либо нам нужна более сложная логика, доступная в методе &lt;b&gt;Generate&lt;/b&gt;.&lt;/font&gt;&lt;/p&gt;
&lt;p align="justify"&gt;Причина такого поведения кроется в том, что енумератор класса &lt;b&gt;List&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;of&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;T&lt;/b&gt; (а также большинства других коллекций BCL) является структурой, а не классом. А, как нам известно, изменяемые структуры (ведь енумератор изменяет свое внутреннее состояние) не очень вяжется с передачей по значению. В результате этого мы постоянно пробуем изменить копию енумератора, а не исходный енумертор, переданный нами в методе &lt;b&gt;Generate&lt;/b&gt;.&lt;/p&gt;
&lt;p align="justify"&gt;Изменяемые структуры – это зло, и только стремление к оптимизации кода для наиболее распространенного сценария использования енумераторов привели к тому, что разработчики BCL сделали их структурами, а не классами. Поскольку наиболее частым сценарием использования енумерторов в C# 2.0 было использование его в цикле foreach, который никак не страдал от проблем с изменяемыми структурами, то было принято решение сэкономить несколько тактов процессора и использовать для енумератора именно структуру, а не класс.&lt;/p&gt;
&lt;p align="justify"&gt;&lt;font color="#526e66"&gt;&lt;strong&gt;ПРИМЕЧАНИЕ&lt;/strong&gt;&lt;br&gt;Подробности о том, почему это было сделано именно так, можно почитать &lt;/font&gt;&lt;a href="http://blogs.microsoft.co.il/blogs/sasha/archive/2010/07/05/enumerator-structs-optimizing-for-the-common-case.aspx"&gt;в одном из постов&lt;/a&gt;&lt;font color="#526e66"&gt;&lt;/font&gt;&lt;font color="#526e66"&gt; Sasha Goldshtein, а также &lt;/font&gt;&lt;a href="http://stackoverflow.com/questions/3168311/why-bcl-collections-use-struct-enumerators-not-classes"&gt;в ответе&lt;/a&gt;&lt;font color="#526e66"&gt;&lt;/font&gt;&lt;font color="#526e66"&gt; Эрика Липперта на stackoverflow. О других же проблемах изменяемых значимых типов можно почитать в статье: &lt;/font&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2011/07/blog-post.html"&gt;О вреде изменяемых значимых типов&lt;/a&gt;&lt;font color="#526e66"&gt;&lt;/font&gt;&lt;font color="#526e66"&gt;.&lt;/font&gt;&lt;/p&gt;
&lt;p&gt;Решается эта проблема довольно просто: для этого достаточно привести исходное значение енумератора к интерфейсу, что приведет к его упаковке и последующей модификации «общей» переменной, расположенной в управляемой куче, а не к изменению копии:&lt;pre style="font-family: consolas; background: white; color: black; font-size: 13px"&gt;&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; li = &lt;span style="color: blue"&gt;new&lt;/span&gt;&amp;nbsp;&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; {1, 2, 3};&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;&lt;span style="color: #2b91af"&gt;IObservable&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; oi = &lt;span style="color: #2b91af"&gt;Observable&lt;/span&gt;.Generate(&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green"&gt;/*initialState:*/&lt;/span&gt; &lt;strong&gt;(&lt;span style="color: #2b91af"&gt;IEnumerator&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt;)&lt;/strong&gt;li.GetEnumerator(),&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green"&gt;/*condition:*/&lt;/span&gt; e =&amp;gt; e.MoveNext(),&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green"&gt;/*iterate:*/&lt;/span&gt; e =&amp;gt; e,&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green"&gt;/*resultSelector:*/&lt;/span&gt; e =&amp;gt; e.Current&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; );
 
oi.Subscribe(i =&amp;gt; &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515"&gt;"i: &lt;/span&gt;&lt;span style="color: mediumseagreen"&gt;{0}&lt;/span&gt;&lt;span style="color: #a31515"&gt;"&lt;/span&gt;, i));&lt;/pre&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8596733192274108952-7382891056291620728?l=sergeyteplyakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sergeyteplyakov.blogspot.com/feeds/7382891056291620728/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8596733192274108952&amp;postID=7382891056291620728' title='Комментарии: 3'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/7382891056291620728'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/7382891056291620728'/><link rel='alternate' type='text/html' href='http://sergeyteplyakov.blogspot.com/2011/11/observablegenerate.html' title='Observable.Generate и перечисление списков'/><author><name>Sergey Teplyakov</name><uri>https://profiles.google.com/108967431947412296254</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-2weHUILo1ns/AAAAAAAAAAI/AAAAAAAABRo/POab-anD6xA/s512-c/photo.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8596733192274108952.post-8376322488610395693</id><published>2011-11-03T00:24:00.001+02:00</published><updated>2011-11-05T11:57:41.876+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET 4.5'/><category scheme='http://www.blogger.com/atom/ns#' term='Exception handling'/><title type='text'>Повторная генерация исключений</title><content type='html'>&lt;p align="justify"&gt;Обработка исключений появилась в mainstream языках программирования вот уже более трех десятилетий назад, но сегодня все еще можно встретить разработчиков, которые боятся их использовать. Некоторые считают, что генерация исключений в конструкторе повредит их хрупкой карме и их настигнет кара в виде поддержки кода двадцатилетней давности, написанного стадом безумных индусов. Некоторые все еще застряли в эпохе языка С и даже в языке C# интенсивно используют коды возврата в виде магических чисел или даже строк, считая, что исключения придумали трусы, а настоящие самураи могут обойтись и без них. (Хотя мы-то с вами знаем, что настоящие самураи следуют &lt;a href="http://sergeyteplyakov.blogspot.com/2011/09/blog-post_13.html"&gt;“Принципу самурая”&lt;/a&gt; и никаких кодов возврата не используют).  &lt;p align="justify"&gt;Дополнительную сложность добавляют конкретные платформы и языки программирования. Сегодня на собеседовании C# разработчика, когда речь заходит об обработке исключений, обязательно прозвучит вопрос: “А в чем отличие “проброса” исключения с помощью конструкций &lt;b&gt;throw;&lt;/b&gt; и &lt;b&gt;throw ex;&lt;/b&gt;?”. И хотя, это страшный баян и большинство разработчиков давно знают правильный ответ на этот вопрос, в реальном коде встретить “некошерный” вариант очень даже просто.  &lt;p align="justify"&gt;Особенность платформы .NET заключается в том, что в ней не существует (а точнее, как мы вскоре увидим – не существовало) способа перехвата исключения в одном месте и последующего его генерирования в другом. Если разработчик бизнес-приложения или библиотеки сталкивался с такой задачей, то решалась она очень простым способом: исходное исключение заворачивалось в другой объект в виде вложенного исключения и пробрасывалось уже новое исключение. &lt;/p&gt;&lt;a name='more'&gt;&lt;/a&gt; &lt;p align="justify"&gt;Давайте рассмотрим такой пример. Предположим у нас есть кастомный класс исключения по имени &lt;b&gt;CustomException&lt;/b&gt;, а также простой класс &lt;b&gt;SampleClass&lt;/b&gt;, конструктор которого генерирует это самое исключение.&lt;pre style="font-family: consolas; background: white; color: black; font-size: 13px"&gt;&lt;span style="color: green"&gt;// Простое кастомное исключение, чтобы было, что перехватывать&lt;/span&gt;
&lt;span style="color: blue"&gt;class&lt;/span&gt;&amp;nbsp;&lt;span style="color: #2b91af"&gt;CustomException&lt;/span&gt; : &lt;span style="color: #2b91af"&gt;Exception&lt;/span&gt; { }
 
&lt;span style="color: blue"&gt;class&lt;/span&gt;&amp;nbsp;&lt;span style="color: #2b91af"&gt;SampleClass&lt;/span&gt;
{&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;    &lt;span style="color: green"&gt;// Совершенно бесполезный класс,&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;    &lt;span style="color: green"&gt;// конструктор которого только и делает, что бросает исключение&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br&gt;    &lt;span style="color: blue"&gt;public&lt;/span&gt; SampleClass() { &lt;span style="color: blue"&gt;throw&lt;/span&gt;&amp;nbsp;&lt;span style="color: blue"&gt;new&lt;/span&gt;&amp;nbsp;&lt;span style="color: #2b91af"&gt;CustomException&lt;/span&gt;(); }
}&lt;/pre&gt;&lt;font color="#008000" face="Consolas"&gt;&lt;/font&gt;
&lt;p align="justify"&gt;Теперь давайте создадим объект этого класса с помощью generic метода, а также сделаем синглтон этого класса:&lt;pre style="font-family: consolas; background: white; color: black; font-size: 13px"&gt;&lt;span style="color: green"&gt;// Простой фабричный метод, создающий экземпляр объекта&lt;/span&gt;
&lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style="color: blue"&gt;static&lt;/span&gt; T CreateInstance&amp;lt;T&amp;gt;() &lt;span style="color: blue"&gt;where&lt;/span&gt; T : &lt;span style="color: blue"&gt;new&lt;/span&gt;()
{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;return&lt;/span&gt;&amp;nbsp;&lt;span style="color: blue"&gt;new&lt;/span&gt; T();
}
 
&lt;span style="color: green"&gt;// Простой класс синглтона&lt;/span&gt;
&lt;span style="color: blue"&gt;class&lt;/span&gt;&amp;nbsp;&lt;span style="color: #2b91af"&gt;SampleClassSingleton&lt;/span&gt;
{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;private&lt;/span&gt;&amp;nbsp;&lt;span style="color: blue"&gt;static&lt;/span&gt;&amp;nbsp;&lt;span style="color: #2b91af"&gt;SampleClass&lt;/span&gt; _instance = &lt;span style="color: blue"&gt;new&lt;/span&gt;&amp;nbsp;&lt;span style="color: #2b91af"&gt;SampleClass&lt;/span&gt;();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;static&lt;/span&gt; SampleClassSingleton() { }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style="color: blue"&gt;static&lt;/span&gt;&amp;nbsp;&lt;span style="color: #2b91af"&gt;SampleClass&lt;/span&gt; Instance { &lt;span style="color: blue"&gt;get&lt;/span&gt; { &lt;span style="color: blue"&gt;return&lt;/span&gt; _instance; } }
}&lt;/pre&gt;
&lt;p align="justify"&gt;Вопрос в следующем, какой блок &lt;b&gt;catch&lt;/b&gt; будет выполнен при вызове метода &lt;b&gt;CreateInstance&amp;lt;&lt;/b&gt;&lt;b&gt;SampleClass&amp;gt;()&lt;/b&gt; или при обращении к &lt;b&gt;SampleClassSingleton.&lt;/b&gt;&lt;b&gt;Instance&lt;/b&gt;?&lt;pre style="font-family: consolas; background: white; color: black; font-size: 13px"&gt;&lt;span style="color: blue"&gt;try&lt;/span&gt;
{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; CreateInstance&amp;lt;&lt;span style="color: #2b91af"&gt;SampleClass&lt;/span&gt;&amp;gt;();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green"&gt;// или&lt;br&gt; &lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;var&lt;/span&gt; instance = &lt;span style="color: #2b91af"&gt;SampleClassSingleton&lt;/span&gt;.Instance;
}
&lt;span style="color: blue"&gt;catch&lt;/span&gt; (&lt;span style="color: #2b91af"&gt;CustomException&lt;/span&gt; e)
{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(e);
}
&lt;span style="color: blue"&gt;catch&lt;/span&gt; (&lt;span style="color: #2b91af"&gt;Exception&lt;/span&gt; e)
{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(e);
}&lt;/pre&gt;
&lt;p align="justify"&gt;Не думаю, что для кого-то этот код будет большим откровением, но в обоих случаях “ожидаемый” блок &lt;b&gt;catch(CustomException e)&lt;/b&gt; выполнен не будет, вместо этого будет выполнен блок &lt;b&gt;catch(Exception e)&lt;/b&gt;. 
&lt;p align="justify"&gt;В первом случае (т.е. при использовании generic метода &lt;b&gt;CreateInstance&lt;/b&gt;) на самом деле конструктор по умолчанию не вызывается напрямую, вместо этого используется &lt;b&gt;Activator.CreateInstance&lt;/b&gt;, который оборачивает исходное исключение в &lt;b&gt;TargetInvocationException&lt;/b&gt;. Именно поэтому срабатывает блок &lt;b&gt;catch(Exception e)&lt;/b&gt;, поскольку &lt;b&gt;TargetInvocationException&lt;/b&gt; никак не подходит нашему первому блоку обработки исключений. 
&lt;p align="justify"&gt;&lt;b&gt;&lt;font color="#526e66"&gt;ПРЕДУПРЕЖДЕНИЕ&lt;/font&gt;&lt;/b&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;font color="#526e66"&gt;Не пишите в логи только e.Message, поскольку когда ляжет ваш продакш сервер от этой информации вам будет ни холодно ни жарко, поскольку наиболее ценная информация может таиться в одном из вложенных исключений. Даже в составе .Net Framework существует множество мест, которые оборачивают исходное исключение и пробрасывают его в качестве вложенного, ни говоря уже за кастомный код, который может обернуть нужное вам исключение в десяток вложенных.&lt;/font&gt;&lt;/p&gt;
&lt;p align="justify"&gt;При использовании синглтона, ситуация аналогична: поскольку инициализация поля &lt;b&gt;_instance&lt;/b&gt; происходит в статическом конструкторе, то любые исключения, которые происходят во время его выполнения делают тип «невалидным». В результате, все последующие обращения к этому типу приводят к генерации исключения &lt;b&gt;TypeLoadException&lt;/b&gt;&lt;b&gt; &lt;/b&gt;с соответствующим вложенным исключением 
&lt;p align="justify"&gt;&lt;b&gt;&lt;font color="#526e66"&gt;ПРИМЕЧАНИЕ&lt;/font&gt;&lt;/b&gt;&lt;b&gt;&lt;br&gt;&lt;/b&gt;&lt;font color="#526e66"&gt;Подробнее о том, для чего нужен пустой статический конструктор и к каким проблемам может привести его отсутствие см. &lt;a href="http://sergeyteplyakov.blogspot.com/2011/08/blog-post.html"&gt;О синглтонах и статических конструторах&lt;/a&gt;&lt;/font&gt;&lt;font color="#526e66"&gt;.&lt;/font&gt;&lt;/p&gt;
&lt;p align="justify"&gt;Поведение в обоих случаях является хорошо документированным и известным, так что если когда-то его могли считать багом, то теперь это уже фича. Но исправить эту ситуацию, к сожалению, мы не можем. Даже если мы перехватим исключение в методе &lt;b&gt;CreateInstance&lt;/b&gt; и постараемся пробросить вложенное исключение, то мы потеряем стек вызовов, что сделает результирующее исключение значительно менее полезным:&lt;pre style="font-family: consolas; background: white; color: black; font-size: 13px"&gt;&lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style="color: blue"&gt;static&lt;/span&gt; T CreateInstance&amp;lt;T&amp;gt;() &lt;span style="color: blue"&gt;where&lt;/span&gt; T : &lt;span style="color: blue"&gt;new&lt;/span&gt;()
{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;try&lt;br&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;return&lt;/span&gt;&amp;nbsp;&lt;span style="color: blue"&gt;new&lt;/span&gt; T();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;catch&lt;/span&gt; (&lt;span style="color: #2b91af"&gt;TargetInvocationException&lt;/span&gt; e)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green"&gt;// Исходный стек вызовов потерян, теперь все будут думать, что виноваты мы!&lt;br&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;throw&lt;/span&gt; e.InnerException;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
}&lt;/pre&gt;
&lt;p align="justify"&gt;Аналогичная проблема ждет нас, если мы попробуем сохранить исключение при создании экземпляра _&lt;b&gt;instance&lt;/b&gt; и будем пробрасывать его при обращении к свойству &lt;b&gt;Instance&lt;/b&gt;. (Да, отложенная инициализация решит эту конкретную проблему, но сейчас речь не об этом). 
&lt;p align="justify"&gt;Однако в .Net Framewor 4.5 появился класс, способный помочь в решении этой проблемы. Это класс &lt;b&gt;ExceptionDispatchInfo&lt;/b&gt;, способный сохранить исходное исключение и потом пробросить его заново не теряя информацию о стеке вызовов. Конечно, основной смысл его применения не связан с “выпрямлением” статических конструкторов или метода &lt;b&gt;Activator.CreateInstance&lt;/b&gt;. Главная его задача заключается в решении вопросов асинхронности и многопоточности, когда исключение происходит в одном потоке, а пробрасывается в другом. 
&lt;p align="justify"&gt;Давайте рассмотрим следующий код: &lt;pre style="font-family: consolas; background: white; color: black; font-size: 13px"&gt;&lt;span style="color: #2b91af"&gt;Task&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; task = &lt;span style="color: #2b91af"&gt;Task&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt;.Factory.StartNew(() =&amp;gt; { &lt;span style="color: blue"&gt;throw&lt;/span&gt;&amp;nbsp;&lt;span style="color: blue"&gt;new&lt;/span&gt;&amp;nbsp;&lt;span style="color: #2b91af"&gt;CustomException&lt;/span&gt;(); });
&lt;span style="color: blue"&gt;try&lt;/span&gt;
{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;int&lt;/span&gt; result = task.Result;
}
&lt;span style="color: blue"&gt;catch&lt;/span&gt; (&lt;span style="color: #2b91af"&gt;CustomException&lt;/span&gt; e)
{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green"&gt;// Неа, сюда нам с вами не попасть:(&lt;br&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515"&gt;"CustomException caught: "&lt;/span&gt; + e);
}
&lt;span style="color: blue"&gt;catch&lt;/span&gt; (&lt;span style="color: #2b91af"&gt;AggregateException&lt;/span&gt; e)
{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green"&gt;// А вот и наше исходное исключение!&lt;br&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;var&lt;/span&gt; inner = e.GetBaseException();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515"&gt;"Aggregate exception caught: "&lt;/span&gt; + inner);
}&lt;/pre&gt;
&lt;p align="justify"&gt;Если “задача” падает с исключением, то исходное исключение (в нашем случае &lt;b&gt;CustomException&lt;/b&gt;) будет завернуто в &lt;b&gt;AggregateException&lt;/b&gt;. Причин такому поведению несколько: во-первых, хотя задача по своей природе представляет собой оболочку над некоторой длительной асинхронной операцией с единственным результатом, в некоторых случаях результат одной задачи может основываться на результатах параллельного выполнения нескольких задач. Например, мы можем объединить несколько задач в одну с помощью &lt;b&gt;Task&lt;/b&gt;&lt;b&gt;.&lt;/b&gt;&lt;b&gt;WaitAll&lt;/b&gt; или мы можем связать несколько задач с помощью продолжений. Второй причиной такого поведения является невозможность проброса исходного исключения без искажения его стека вызовов. 
&lt;p align="justify"&gt;Однако после появления в языке C# 5.0 новых возможностей по работе с асинхронностью, приоритеты несколько изменились. Одной из главных фич ключевых слов &lt;b&gt;await&lt;/b&gt; и &lt;b&gt;async&lt;/b&gt; является простота преобразования синхронного кода в асинхронный, но помимо «выпрямления» потока исполнения (который даже в случае применение задач оставляет желать лучшего), должна быть решена и задача обработки исключений. Поэтому при получении результатов задачи с помощью ключевого слова &lt;b&gt;await&lt;/b&gt;, исходное &lt;b&gt;AggregateException&lt;/b&gt; разворачивается и пробрасывается исходное исключение:&lt;pre style="font-family: consolas; background: white; color: black; font-size: 13px"&gt;&lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style="color: blue"&gt;static async&lt;/span&gt;&amp;nbsp;&lt;span style="color: blue"&gt;void&lt;/span&gt; SimpleTask()
{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #2b91af"&gt;Task&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; task = &lt;span style="color: #2b91af"&gt;Task&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt;.Factory.StartNew(() =&amp;gt; { &lt;span style="color: blue"&gt;throw&lt;/span&gt;&amp;nbsp;&lt;span style="color: blue"&gt;new&lt;/span&gt;&amp;nbsp;&lt;span style="color: #2b91af"&gt;CustomException&lt;/span&gt;(); });&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;try&lt;br&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green"&gt;// await «разворачивает» исходное исключение сгенерированное внутри задачи&lt;br&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green"&gt;// и пробрасывает именно его, а не AggregateException!&lt;br&gt; &lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;int&lt;/span&gt; result = &lt;span style="color: blue"&gt;await&lt;/span&gt; task;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;catch&lt;/span&gt; (&lt;span style="color: #2b91af"&gt;CustomException&lt;/span&gt; e)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green"&gt;// Теперь вызывается этот обработчик, как и в случае синхронного вызова&lt;br&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515"&gt;"CustomException caught: "&lt;/span&gt; + e);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
}&lt;/pre&gt;
&lt;p align="justify"&gt;&lt;font color="#526e66"&gt;&lt;b&gt;ПРИМЕЧАНИЕ&lt;/b&gt;&lt;br&gt;Если вы не знакомы такими фичами языка C# 5.0, как &lt;b&gt;async&lt;/b&gt; и &lt;b&gt;await&lt;/b&gt;, то восполнить этот пробел можно с помощью статьи: &lt;/font&gt;&lt;a href="http://sergeyteplyakov.blogspot.com/2010/12/c-5.html"&gt;&lt;font color="#526e66"&gt;Асинхронные операции в C# 5&lt;/font&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p align="justify"&gt;Как уже было сказано, реализовано такое поведение с помощью нового класса &lt;b&gt;ExceptionDispatchInfo&lt;/b&gt;, который позволяет сохранить исходное исключение и пробросить его позднее, возможно даже в новом потоке. Причем реализовано это настолько просто, что нам не составит труда сделать это самостоятельно. 
&lt;p align="justify"&gt;Для начала давайте для класса T&lt;b&gt;ask&lt;/b&gt;&lt;b&gt;&amp;lt;&lt;/b&gt;&lt;b&gt;T&lt;/b&gt;&lt;b&gt;&amp;gt; &lt;/b&gt;создадим класс с методом расширения &lt;b&gt;GetResult&lt;/b&gt;, который будет очень похож на свойство &lt;b&gt;Result&lt;/b&gt;, но будет «выпрямлять» &lt;b&gt;AggregateException&lt;/b&gt; и пробрасывать вложенное исключение без потери стека вызовов. 
&lt;p align="justify"&gt;&lt;font color="#526e66"&gt;&lt;b&gt;ПРИМЕЧАНИЕ&lt;/b&gt;&lt;br&gt;Такое поведение уже реализовано в составе .Net Framework 4.5 с помощью &lt;b&gt;Task&lt;/b&gt;&lt;b&gt;&amp;lt;&lt;/b&gt;&lt;b&gt;T&lt;/b&gt;&lt;b&gt;&amp;gt;.&lt;/b&gt;&lt;b&gt;GetAwaiter&lt;/b&gt;&lt;b&gt;().&lt;/b&gt;&lt;b&gt;GetResult&lt;/b&gt;&lt;b&gt;()&lt;/b&gt;, но давайте забудем об этом и сделаем тоже самое самостоятельно.&lt;/font&gt;&lt;/p&gt;
&lt;p align="justify"&gt;Пользоваться классом &lt;b&gt;ExceptionDispatchInfo&lt;/b&gt; довольно просто: для этого достаточно захватить исключение в одном месте с помощью статического метода &lt;b&gt;Capture&lt;/b&gt;, а затем пробросить это исключение в другом месте (и, возможно даже в другом потоке) с помощью метода &lt;b&gt;Throw&lt;/b&gt;.&lt;pre style="font-family: consolas; background: white; color: black; font-size: 13px"&gt;&lt;span style="color: blue"&gt;static&lt;/span&gt;&amp;nbsp;&lt;span style="color: blue"&gt;class&lt;/span&gt;&amp;nbsp;&lt;span style="color: #2b91af"&gt;TaskExtensions&lt;/span&gt;
{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style="color: blue"&gt;static&lt;/span&gt; T GetResult&amp;lt;T&amp;gt;(&lt;span style="color: blue"&gt;this&lt;/span&gt;&amp;nbsp;&lt;span style="color: #2b91af"&gt;Task&lt;/span&gt;&amp;lt;T&amp;gt; task)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;try&lt;br&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; T result = task.Result;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;return&lt;/span&gt; result;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;catch&lt;/span&gt; (&lt;span style="color: #2b91af"&gt;AggregateException&lt;/span&gt; e)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #2b91af"&gt;ExceptionDispatchInfo&lt;/span&gt; di = &lt;span style="color: #2b91af"&gt;ExceptionDispatchInfo&lt;/span&gt;.Capture(e.InnerException);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; di.Throw();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;return&lt;/span&gt;&amp;nbsp;&lt;span style="color: blue"&gt;default&lt;/span&gt;(T);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
}&lt;/pre&gt;
&lt;p align="justify"&gt;Теперь, если изменить предыдущий фрагмент кода и заменить &lt;b&gt;task&lt;/b&gt;&lt;b&gt;.&lt;/b&gt;&lt;b&gt;Result&lt;/b&gt; на вызов метода &lt;b&gt;Task&lt;/b&gt;&lt;b&gt;.&lt;/b&gt;&lt;b&gt;GetResult&lt;/b&gt;, то мы сможем перехватывать конкретный тип исключения вместо исключения &lt;b&gt;AggregateException&lt;/b&gt;.&lt;pre style="font-family: consolas; background: white; color: black; font-size: 13px"&gt;&lt;span style="color: #2b91af"&gt;Task&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; task = &lt;span style="color: #2b91af"&gt;Task&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt;.Factory.StartNew(() =&amp;gt; { &lt;span style="color: blue"&gt;throw&lt;/span&gt;&amp;nbsp;&lt;span style="color: blue"&gt;new&lt;/span&gt;&amp;nbsp;&lt;span style="color: #2b91af"&gt;CustomException&lt;/span&gt;(); });
&lt;span style="color: blue"&gt;try&lt;/span&gt;
{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;int&lt;/span&gt; result = task.GetResult();
}
&lt;span style="color: blue"&gt;catch&lt;/span&gt; (&lt;span style="color: #2b91af"&gt;CustomException&lt;/span&gt; e)
{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green"&gt;// Теперь мы можем перехватывать CustomException, а не AggregateException&lt;br&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515"&gt;"CustomException caught: "&lt;/span&gt; + e);
}
&lt;/pre&gt;
&lt;p align="justify"&gt;Аналогичным образом можно изменить наш метод &lt;b&gt;CreateInstance&lt;/b&gt;, который будет перехватывать исключение &lt;b&gt;TargetInvocationException&lt;/b&gt; и пробрасывать вложенное исключение:&lt;pre style="font-family: consolas; background: white; color: black; font-size: 13px"&gt;&lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;nbsp;&lt;span style="color: blue"&gt;static&lt;/span&gt; T CreateInstance&amp;lt;T&amp;gt;() &lt;span style="color: blue"&gt;where&lt;/span&gt; T : &lt;span style="color: blue"&gt;new&lt;/span&gt;()
{&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;try&lt;br&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;var&lt;/span&gt; t = &lt;span style="color: blue"&gt;new&lt;/span&gt; T();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;return&lt;/span&gt; t;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;catch&lt;/span&gt; (&lt;span style="color: #2b91af"&gt;TargetInvocationException&lt;/span&gt; e)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green"&gt;// Захватываем вложенное исключение в ExceptionDispatchInfo&lt;br&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: #2b91af"&gt;ExceptionDispatchInfo&lt;/span&gt; di = &lt;span style="color: #2b91af"&gt;ExceptionDispatchInfo&lt;/span&gt;.Capture(e.InnerException);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green"&gt;// Пробрасываем это исключение с сохранением всей информации&lt;br&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; di.Throw();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green"&gt;// Компилятор не знает, что di.Throws() всегда генерит исключение, поэтому&lt;br&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green"&gt;// без этой строки кода мы получим ошибку компиляции, что метод не всегда&lt;br&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: green"&gt;// возвращает результат&lt;br&gt;&lt;/span&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;span style="color: blue"&gt;return&lt;/span&gt;&amp;nbsp;&lt;span style="color: blue"&gt;default&lt;/span&gt;(T);&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }
}
&lt;/pre&gt;
&lt;p align="justify"&gt;Теперь, при попытке вызвать этот метод из функции &lt;b&gt;Main&lt;/b&gt; мы получим вразумительное исключение с нормальным стеком исполнения: 
&lt;p align="justify"&gt;&lt;font face="Courier New"&gt;ConsoleApplication1.CustomException: Exception of type 'ConsoleApplication1.CustomException' was thrown.&lt;br&gt;at ConsoleApplication1.SampleClass..ctor() in &lt;br&gt;c:\Projects\ConsoleApplication1\ConsoleApplication1\Program.cs:line 19&lt;br&gt;&lt;/font&gt;&lt;font face="Courier New"&gt;&lt;b&gt;--- End of stack trace from previous location where exception was thrown ---&lt;br&gt;&lt;/b&gt;at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()&lt;br&gt;at ConsoleApplication1.Program.CreateInstance[T]() in c&lt;br&gt;\Projects\ConsoleApplication1\ConsoleApplication1\Program.cs:&lt;br&gt;line 50&lt;br&gt;at ConsoleApplication1.Program.Main(String[] args) in c:\ \Projects\ConsoleApplication1\ConsoleApplication1\Program.cs:line 63&lt;/font&gt; 
&lt;p align="justify"&gt;Класс синглтона можно реализовать подобным образом. 
&lt;p align="justify"&gt;Класс &lt;b&gt;ExceptionDispatchInfo&lt;/b&gt; едва ли будет киллер-фичей новой версии .Net Framework, однако с наступлением эры асинхронности у него точно найдется достойная область применения. Так, например, он уже используется в библиотеке реактивных расширений для реализации паттерна &lt;b&gt;await&lt;/b&gt; (эта реализация доступна только в экспериментальном релизе) и может использоваться всеми, кто захочет реализовать этот паттерн самостоятельно. &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8596733192274108952-8376322488610395693?l=sergeyteplyakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sergeyteplyakov.blogspot.com/feeds/8376322488610395693/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8596733192274108952&amp;postID=8376322488610395693' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/8376322488610395693'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/8376322488610395693'/><link rel='alternate' type='text/html' href='http://sergeyteplyakov.blogspot.com/2011/11/blog-post_03.html' title='Повторная генерация исключений'/><author><name>Sergey Teplyakov</name><uri>https://profiles.google.com/108967431947412296254</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-2weHUILo1ns/AAAAAAAAAAI/AAAAAAAABRo/POab-anD6xA/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8596733192274108952.post-506985740435987957</id><published>2011-10-26T23:54:00.001+03:00</published><updated>2011-10-26T23:54:52.970+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Философия программирования'/><category scheme='http://www.blogger.com/atom/ns#' term='WCF'/><title type='text'>Проблемы передачи списка перечислений или Почему абстракции «текут»</title><content type='html'>&lt;p align="right"&gt;&lt;em&gt;Все нетривиальные абстракции дырявы     &lt;br /&gt;Джоэл Спольски – Закон дырявых абстракций&lt;/em&gt; &lt;/p&gt;  &lt;p align="right"&gt;&lt;em&gt;А иногда дырявятся и довольно простые абстракции     &lt;br /&gt;Автор этой статьи&lt;/em&gt; &lt;/p&gt;  &lt;p align="justify"&gt;Большинство современных разработчиков знакомы с «законом дырявых абстракций» по &lt;a href="http://russian.joelonsoftware.com/Articles/LeakyAbstractions.html"&gt;знаменитой заметке&lt;/a&gt; Джоэла Спольски с одноименным названием. Заключается этот закон в том, что как бы ни был хорош протокол взаимодействия, фреймворк или набор классов, моделирующих предметную область, рано или поздно нам приходится спускаться на уровень ниже и разбираться с тем, как же эта абстракция устроена. Внутреннее устройство абстракции должно быть проблемой самой абстракции, но возможно это только в наиболее общих случаев и лишь до тех пор, пока все идет хорошо (*). &lt;/p&gt;  &lt;p align="justify"&gt;Когда-то давно, в «небольшой» мелкомягкой компании решили, а почему бы нам не «абстрагироваться» от местоположения объекта и сделать сам факт того, является ли объект локальным или удаленным, лишь «деталью реализации». Так появились технологии DCOM и ее наследник .NET Remoting, которые скрывали от разработчика, является ли объект удаленным или нет. При этом появились все эти «прозрачные прокси», которые позволяли работать с удаленным объектом, даже не зная об этом. Однако, со временем выяснилось, что эта информация архиважна для разработчика, поскольку удаленный объект может генерировать совершенно другой перечень исключений, да и стоимость работы с ним несравнимо выше, чем взаимодействие с локальным объектом. &lt;/p&gt; &lt;a name='more'&gt;&lt;/a&gt; &lt;p align="justify"&gt;Конечно, такое «сокрытие информации» бывает полезным, однако в общем случае это приводит скорее к усложнению жизни разработчика, а не к ее упрощению. Именно поэтому новая версия технологии для разработки распределенных приложений под названием WCF от такой практики ушла и, хотя грань между локальным и удаленным объектом осталась очень тонкой, но она все же, осталась. &lt;/p&gt;  &lt;p align="justify"&gt;Подобных примеров, когда нам нужно знать не только видимое поведение (абстракцию), но и понимать внутреннее устройство (реализацию), довольно много. В большинстве языков программирования работа с разными типами коллекций делается очень похожим образом. Коллекции могут «прятаться» за базовыми классами или интерфейсами (как в .NET), или использовать какой-то другой способ обощения (как, например, в языке С++). Но, несмотря на то, что мы можем работать с разными коллекции практически одинаково, мы не можем полностью «отвязать» наши классы от конкретных типов коллекций. Несмотря на видимое сходство, нам нужно понимать, что лучше использовать в данный момент: вектор или двусвязный список, hash-set или sorted set. От внутренней реализации коллекции зависят сложности основных операций: поиска элемента, вставки в середину или в конец коллекции и знать о таких различиях просто необходимо. &lt;/p&gt;  &lt;p align="justify"&gt;Давайте рассмотрим конкретный пример. Все мы знаем, что такие типы как &lt;b&gt;List&amp;lt;&lt;/b&gt;&lt;b&gt;T&amp;gt;&lt;/b&gt; (или &lt;b&gt;std::&lt;/b&gt;&lt;b&gt;vector&lt;/b&gt; в С++) реализованы на основе простого массива. Если коллекция уже заполнена, то при добавлении нового элемента будет создан новый внутренний массив, при этом он «вырастит» не на один элемент, а несколько сильнее (**). Многие знают о таком поведении, но в большинстве случаев мы можем не обращать на это никакого внимания: это является «личной проблемой» класса &lt;b&gt;List&amp;lt;&lt;/b&gt;&lt;b&gt;T&amp;gt;&lt;/b&gt; и нам до нет никакого дела. &lt;/p&gt;  &lt;p align="justify"&gt;Но давайте предположим, что нам нужно передать список перечислений (enum-ов) через WCF или просто сериализовать такой список с помощью классов &lt;b&gt;DataContractSerializer&lt;/b&gt; или &lt;b&gt;NetDataContractSerializer&lt;/b&gt;(***). При этом перечисление объявлено следующим образом: &lt;/p&gt;  &lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;enum&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;Color&lt;/span&gt;&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; Green = 1,&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; Red,&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; Blue&lt;br /&gt;}&lt;/pre&gt;

&lt;p align="justify"&gt;Не обращайте внимания на то, что это перечисление не помечено никакими атрибутами, это не является помехой для &lt;b&gt;NeDataContractSerializer&lt;/b&gt;-а. Главная особенность этого перечисления заключается в том, что в нем нет нулевого значения; значения перечислений начинаются с &lt;b&gt;1&lt;/b&gt;. &lt;/p&gt;

&lt;p align="justify"&gt;Особенность сериализации перечислений в WCF заключается в том, что вы не сможете сериализовать значение, не принадлежащее этому перечислению. &lt;/p&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;static&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;string&lt;/span&gt; Serialize&amp;lt;T&amp;gt;(T obj)&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// Используем именно NetDataContractSerializer, хотя в данном случае&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// поведение DataContractSerializer аналогичным&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;var&lt;/span&gt; serializer = &lt;span style="color: blue"&gt;new&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;NetDataContractSerializer&lt;/span&gt;();&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;var&lt;/span&gt; sb = &lt;span style="color: blue"&gt;new&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;StringBuilder&lt;/span&gt;();&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;using&lt;/span&gt; (&lt;span style="color: blue"&gt;var&lt;/span&gt; writer = &lt;span style="color: #2b91af"&gt;XmlWriter&lt;/span&gt;.Create(sb))&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; serializer.WriteObject(writer, obj);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; writer.Flush();&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;return&lt;/span&gt; sb.ToString();&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;}&lt;/pre&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: #2b91af"&gt;Color&lt;/span&gt; color = (&lt;span style="color: #2b91af"&gt;Color&lt;/span&gt;) 55;&lt;br /&gt;Serialize(color);&lt;br /&gt;&lt;/pre&gt;

&lt;p align="justify"&gt;При попытке выполнить этот код, мы получим следующее сообщение об ошибке: &lt;strong&gt;&lt;em&gt;Enum value '55' is invalid for type Color' and cannot be serialized.&lt;/em&gt;&lt;/strong&gt;. Такое поведение является вполне логичным, ведь таким способом мы защищаемся от передачи неизвестных значений между разными приложениями. &lt;/p&gt;

&lt;p align="justify"&gt;Теперь давайте попробуем передать коллекцию из одного элемента:&lt;/p&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;var&lt;/span&gt; colors = &lt;span style="color: blue"&gt;new&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Color&lt;/span&gt;&amp;gt; {&lt;span style="color: #2b91af"&gt;Color&lt;/span&gt;.Green};&lt;br /&gt;&lt;span style="color: blue"&gt;var&lt;/span&gt; s = Serialize(colors);&lt;br /&gt;&lt;/pre&gt;

&lt;p align="justify"&gt;Однако этот, с виду вполне безобидный код, также приводит к ошибке времени выполнения с тем же самым содержанием и отличие заключается лишь в том, что сериализатор не может справиться со значением перечисления, равным &lt;b&gt;0&lt;/b&gt;. На что за … Откуда мог вообще взялся &lt;b&gt;0&lt;/b&gt;? Мы ведь пытаемся передать простую коллекцию с одним элементом, при этом значение этого элемента абсолютно корректно. Однако &lt;b&gt;DataContractSerializer/&lt;/b&gt;&lt;b&gt;NetDataContractSerializer&lt;/b&gt;, как и старая добрая бинарная сериализация, использует рефлексию для получения доступа ко всем полям. В результате чего, все внутреннее представление объекта, которое содержится как в открытых, так и закрытых полях, будет сериализовано в выходной поток. &lt;/p&gt;

&lt;p align="justify"&gt;Поскольку класс &lt;b&gt;List&amp;lt;&lt;/b&gt;&lt;b&gt;T&amp;gt;&lt;/b&gt; построен на основе массива, то при сериализации будет сериализован массив целиком, не зависимо от того, сколько элементов содержится в списке. Так, например, при сериализации коллекции из двух элементов:&lt;/p&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;var&lt;/span&gt; list = &lt;span style="color: blue"&gt;new&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: blue"&gt;int&lt;/span&gt;&amp;gt; {1, 2};&lt;br /&gt;&lt;span style="color: blue"&gt;string&lt;/span&gt; s = Serialize(list);&lt;/pre&gt;

&lt;p align="justify"&gt;В выходном потоке мы получим не два элемента, как мы могли бы ожидать, а 4 (т.е. количество элементов, соответствующих свойству &lt;b&gt;Capacity&lt;/b&gt;, а не &lt;b&gt;Count&lt;/b&gt;): &lt;/p&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;ArrayOfint&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;	&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;_items&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;#160;&lt;/span&gt;&lt;span style="color: red"&gt;z:Id&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;2&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;&amp;#160;&lt;/span&gt;&lt;span style="color: red"&gt;z:Size&lt;/span&gt;&lt;span style="color: blue"&gt;=&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;4&lt;/span&gt;&amp;quot;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;		&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;int&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;1&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;int&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;		&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;int&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;2&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;int&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;		&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;int&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;0&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;int&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;		&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;int&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;0&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;int&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;	&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;_items&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;	&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;_size&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;2&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;_size&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;	&amp;lt;&lt;/span&gt;&lt;span style="color: #a31515"&gt;_version&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;2&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;_version&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;&amp;lt;/&lt;/span&gt;&lt;span style="color: #a31515"&gt;ArrayOfint&lt;/span&gt;&lt;span style="color: blue"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

&lt;p align="justify"&gt;В таком случае, причина сообщения об ошибке, которое возникает при сериализации списка перечислений, становится понятной. Наше перечисление &lt;b&gt;Color&lt;/b&gt; не содержит значение, равного &lt;b&gt;0&lt;/b&gt;, а именно таким значением заполняются элементы внутреннего массива списка: &lt;/p&gt;

&lt;p&gt;&lt;a href="http://lh5.ggpht.com/-JyxLJdAlV80/TqhzfykhYgI/AAAAAAAABUs/PXipQdvKrGk/s1600-h/image%25255B2%25255D.png"&gt;&lt;img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh3.ggpht.com/-IvgA7vlEiIw/TqhzgQiluUI/AAAAAAAABUw/hxl7mfpeYK8/image_thumb.png?imgmax=800" width="431" height="183" /&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p align="justify"&gt;Это еще один пример «протекания» абстракции, когда внутренняя реализация даже такого простого класса, как &lt;b&gt;List&amp;lt;&lt;/b&gt;&lt;b&gt;T&amp;gt;&lt;/b&gt; может помешать нам его нормально сериализовать. &lt;/p&gt;

&lt;h4&gt;Решение проблемы &lt;/h4&gt;

&lt;p align="justify"&gt;Решений у этой проблемы несколько, при этом каждое из решений обладает своими недостатками. &lt;/p&gt;

&lt;h5&gt;&lt;b&gt;1. &lt;/b&gt;&lt;b&gt;Добавление значения по умолчанию&lt;/b&gt; &lt;/h5&gt;

&lt;p align="justify"&gt;Самым простым решением этой проблемы является добавление в перечисление значения, равного &lt;b&gt;0&lt;/b&gt; либо изменить значение одного из существующих элементов:&lt;/p&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;enum&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;Color&lt;/span&gt;&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; None = 0,&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; Green = 1, &lt;span style="color: green"&gt;// или Green = 0&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; Red,&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; Blue&lt;br /&gt;}&lt;/pre&gt;

&lt;p align="justify"&gt;Этот вариант самый простой, однако не всегда возможный; значения перечисления могут соответствовать некоторому значению в базе данных, а добавление фиктивного значения может противоречить бизнес-логике приложения. &lt;/p&gt;

&lt;h5&gt;&lt;b&gt;2. &lt;/b&gt;&lt;b&gt;Передача коллекции без «пустых» элементов&lt;/b&gt; &lt;/h5&gt;

&lt;p align="justify"&gt;Вместо того чтобы делать что-либо с перечислением, можно добиться того, чтобы коллекция не содержала таких пустых элементов. Сделать это можно, например, таким образом:&lt;/p&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;var&lt;/span&gt; li1 = &lt;span style="color: blue"&gt;new&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Color&lt;/span&gt;&amp;gt; { &lt;span style="color: #2b91af"&gt;Color&lt;/span&gt;.Green }; &lt;span style="color: green"&gt;// Содержит еще 3 элемента, равных 0&lt;/span&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;var&lt;/span&gt; li2 = &lt;span style="color: blue"&gt;new&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;List&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Color&lt;/span&gt;&amp;gt;(li1);&lt;/pre&gt;

&lt;p align="justify"&gt;В этом случае, переменная &lt;b&gt;li1&lt;/b&gt; будет содержать три дополнительных пустых элемента (при этом &lt;b&gt;Count&lt;/b&gt; будет равен &lt;b&gt;1&lt;/b&gt;, а &lt;b&gt;Capacity&lt;/b&gt; – &lt;b&gt;4&lt;/b&gt;), а переменная &lt;b&gt;li2&lt;/b&gt; – нет (внутренний массив второго списка будет содержать только &lt;b&gt;1&lt;/b&gt; элемент). &lt;/p&gt;

&lt;p align="justify"&gt;Этот вариант вполне работоспособный, но весьма «хрупкий»: сломать работающий код не составит никакого труда. Безобидное изменение со стороны вашего коллеги в виде удаления ненужной промежуточной коллекции и все, приплыли. &lt;/p&gt;

&lt;h5&gt;&lt;b&gt;3. &lt;/b&gt;&lt;b&gt;Использование других типов коллекций в интерфейсе сервисов&lt;/b&gt; &lt;/h5&gt;

&lt;p align="justify"&gt;Использование других структур данных, таких как массив, либо вместо DataContractSerializer-а использовать XML-сериализацию, которая использует только открытые члены, решит эту проблему. Но насколько это удобно или нет, решать уже вам. &lt;/p&gt;

&lt;p align="justify"&gt;Абстракции текут, точка. Вот почему рыться во внутренней реализации разных библиотек весьма полезно. Даже если эта библиотека отлично скрывает все свои детали, рано или поздно вы столкнетесь с ситуацией, когда без знаний внутреннего ее устройства вы не сможете решить свою проблему. Дебажьте, разбирайтесь с внутренним устройством и не бойтесь, что оно изменится в будущем; не факт, что это вам понадобится, но как минимум, это интересно! &lt;/p&gt;

&lt;p align="justify"&gt;З.Ы. Кстати, дважды подумайте, чтобы передавать значимые типы через WCF в типе &lt;b&gt;List&amp;lt;&lt;/b&gt;&lt;b&gt;T&amp;gt;&lt;/b&gt;. Если у вас будет коллекция из 524-х элементов, то будут переданы еще 500 дополнительных объектов значимого типа! &lt;/p&gt;

&lt;p&gt;--------------------- &lt;/p&gt;

&lt;p align="justify"&gt;(*) Джоэл далеко не первый и не последний автор, предложивший отличную метафору для этих целей. Так, например, Ли Кэмпбел однажды отлично сказал об этом же, но несколько другими словами: «Вы должны понимать как минимум на один уровень абстракции ниже того уровня, на котором кодируете». Подробности в небольшой заметке: &lt;a href="http://ru-quotes.blogspot.com/2010/10/blog-post.html"&gt;О понимании нужного уровня абстракции&lt;/a&gt;. &lt;/p&gt;

&lt;p align="justify"&gt;(**) Обычно подобные структуры данных увеличивают свой внутренний массив в два раза. Так, например, при добавлении элементов в List&amp;lt;T&amp;gt;, «емкость» будет изменяться таким образом: 0, 4, 8, 16, 32, 64, 128, 256, 512, 1024 … &lt;/p&gt;

&lt;p align="justify"&gt;(***) Разница между двумя основными типами сериализаторов WCF достаточно важна. &lt;b&gt;NetDataContractSerializer&lt;/b&gt; в отличие от &lt;b&gt;DataContractSerializer&lt;/b&gt;, нарушает принципы SOA и добавляет информацию о CLR типе в выходной поток, что нарушает «кроссплатформенность» сервис-ориентированной парадигмы. Подробнее об этом можно почитать в заметках: &lt;a href="http://sergeyteplyakov.blogspot.com/2011/02/wcf.html"&gt;Что такое WCF&lt;/a&gt; или &lt;a href="http://sergeyteplyakov.blogspot.com/2011/01/wcf-netdatacontractserializer.html"&gt;Декларативное использование NetDataContractSerializer&lt;/a&gt;.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8596733192274108952-506985740435987957?l=sergeyteplyakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sergeyteplyakov.blogspot.com/feeds/506985740435987957/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8596733192274108952&amp;postID=506985740435987957' title='Комментарии: 15'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/506985740435987957'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/506985740435987957'/><link rel='alternate' type='text/html' href='http://sergeyteplyakov.blogspot.com/2011/10/blog-post_26.html' title='Проблемы передачи списка перечислений или Почему абстракции «текут»'/><author><name>Sergey Teplyakov</name><uri>https://profiles.google.com/108967431947412296254</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-2weHUILo1ns/AAAAAAAAAAI/AAAAAAAABRo/POab-anD6xA/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/-IvgA7vlEiIw/TqhzgQiluUI/AAAAAAAABUw/hxl7mfpeYK8/s72-c/image_thumb.png?imgmax=800' height='72' width='72'/><thr:total>15</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8596733192274108952.post-4864832673547870635</id><published>2011-10-12T22:22:00.001+03:00</published><updated>2011-10-12T22:35:07.995+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='Multithreading'/><title type='text'>Доклады по асинхронному и реактивному программированию</title><content type='html'>&lt;p align="justify"&gt;На следующей неделе я проведу два семинара (или доклада, если хотите) в Киеве. &lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;     &lt;div align="justify"&gt;20.10.11 &lt;a href="http://www.luxoft-training.ru/events/seminar/27211/"&gt;Библиотека реактивных расширений&lt;/a&gt;. Киев, Радищева 10/14, начало в 17-00 (это четверг) &lt;/div&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;div align="justify"&gt;21.10.11 &lt;a href="http://blog.kievalt.net/post/11324222199/async-event"&gt;Асинхронное программирование в .NET&lt;/a&gt;. Kiev ALT.NET, Киев, Амосова 12, 19-й этаж, начало в 18-15 (а это – пятница) &lt;/div&gt;   &lt;/li&gt; &lt;/ul&gt; &lt;a name='more'&gt;&lt;/a&gt;  &lt;h4 align="justify"&gt;&lt;strong&gt;Реактивные расширения&lt;/strong&gt;&lt;/h4&gt;  &lt;p align="justify"&gt;Первый из них пройдет в следующий четверг и будет посвящен реактивному программированию, а если быть более точным, то библиотеке реактивных расширений (a.k.a. Rx – Reactive Extensions). Вход свободный, чтобы попасть на семинар достаточно &lt;a href="http://www.luxoft-training.ru/events/seminar/27211/"&gt;зарегистрироваться&lt;/a&gt;.&lt;/p&gt;  &lt;p align="justify"&gt;План семинара примерно такой (его, если честно, еще добить нужно):&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;     &lt;div align="justify"&gt;Двойственность интерфейсов IEnumerable и IObservable &lt;/div&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;div align="justify"&gt;Обработка событий пользовательского интерфейса &lt;/div&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;div align="justify"&gt;Реактивные расширения и асинхронные операции &lt;/div&gt;   &lt;/li&gt; &lt;/ul&gt;  &lt;p align="justify"&gt;Мне кажется, что понимание философии этой библиотеки значительно важнее знаний синтаксиса или конкретных приемов. Так что именно этой теме будет посвящена добрая треть семинара,&amp;#160; поскольку освоить конкретные вещи, типа работа с событиями или асинхронными операциями значительно проще, когда понятно не только “как”, но и “зачем”. &lt;/p&gt;  &lt;h4 align="justify"&gt;&lt;strong&gt;Асинхронное программирование&lt;/strong&gt;&lt;/h4&gt;  &lt;p align="justify"&gt;Второй доклад будет проходить в пятницу в &lt;a href="http://blog.kievalt.net/post/11324222199/async-event"&gt;рамках Kiev ALT.NET&lt;/a&gt; и будет посвящен асинхронному программированию. По сути, это будет мое четвертое выступление с этой темой (*), так что семинар уже откатан до блеска.&lt;/p&gt;  &lt;p align="justify"&gt;&lt;strong&gt;План семинара&lt;/strong&gt;&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;     &lt;div align="justify"&gt;Синхронные и асинхронные операции &lt;/div&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;div align="justify"&gt;Класс AsyncEnumerator из PowerThreading &lt;/div&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;div align="justify"&gt;Основы TPL &lt;/div&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;div align="justify"&gt;C# 5: async и await &lt;/div&gt;   &lt;/li&gt; &lt;/ul&gt;  &lt;p align="justify"&gt;Поскольку времени будет меньше, чем на предыдущих семинарах, то долго обсуждать паттерны асинхронного программирования в .NET и проблемы синхронного кода мы не будем, а кинемся сразу в бой: на AsyncEnumerator, TPL и новые фишки пятого C#.&lt;/p&gt;  &lt;p align="justify"&gt;Хотя у каждого семинара есть определенная тема я стараюсь (и более того, всячески поощряю) разные отступления, чтобы даже в рамках семинара/доклада обсудить разные интересности. Так, в прошлые разы мы обсуждали самые разные вопросы, начиная от вопросов дизайна языка C# и вопросов “утиной типизации”, встроенной в язык C#, заканчивая способами завершения работы потока и недостатками вызова метода Thread.Abort. Общение получается живым и интересным, и дает пищу для размышлений всем ее участникам.&lt;/p&gt;  &lt;p align="justify"&gt;В общем, приходите, будет интересно!&lt;/p&gt;  &lt;p align="justify"&gt;---------------------------------&lt;/p&gt;  &lt;p align="justify"&gt;(*) О первом своем опыте я уже &lt;a href="http://sergeyteplyakov.blogspot.com/2011/07/net.html"&gt;писал&lt;/a&gt;, первый блин, вроде как, комом не вышел; да и последующие семинары показали, что материал подобран правильно.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8596733192274108952-4864832673547870635?l=sergeyteplyakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sergeyteplyakov.blogspot.com/feeds/4864832673547870635/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8596733192274108952&amp;postID=4864832673547870635' title='Комментарии: 2'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/4864832673547870635'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/4864832673547870635'/><link rel='alternate' type='text/html' href='http://sergeyteplyakov.blogspot.com/2011/10/blog-post.html' title='Доклады по асинхронному и реактивному программированию'/><author><name>Sergey Teplyakov</name><uri>https://profiles.google.com/108967431947412296254</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-2weHUILo1ns/AAAAAAAAAAI/AAAAAAAABRo/POab-anD6xA/s512-c/photo.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8596733192274108952.post-4305682106128262073</id><published>2011-09-27T21:03:00.001+03:00</published><updated>2011-09-27T21:03:47.231+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Шаблоны проектирования'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='ООП'/><title type='text'>Dispose pattern</title><content type='html'>&lt;p align="right"&gt;&lt;i&gt;“Не стоит следовать некоторой идиоме только потому, что так делают все или так где-то написано”      &lt;br /&gt;&lt;/i&gt;&lt;i&gt;Мысли автора статьи во время чтения и рефакторинга чужого кода &lt;/i&gt;&lt;/p&gt;  &lt;p align="justify"&gt;Ни для кого не будет секретом, что платформа .NET поддерживает автоматическое управление памятью. Это значит, что если вы создадите объект с помощью ключевого слова &lt;b&gt;new&lt;/b&gt;, то вам не нужно будет самостоятельно заботиться о его освобождении. Сборщик мусора определит «достижимость» объекта, и если на объект не осталось корневых ссылок, то он будет освобожден. Однако, как только речь заходит о ресурсах, таких как сокет, буфер неуправляемой памяти, дескриптор операционной системы и т.д., то сборщик мусора, по большому счету, умывает руки и весь головняк по работе с такими ресурсами ложится на плечи разработчика.&lt;/p&gt;  &lt;p align="justify"&gt;А как же финализаторы? – спросите вы. Ну, да, есть такое дело, финализаторы действительно предназначены для освобождения ресурсов, но проблема в том, что время их вызова не детерминировано, а это значит, что никто не знает, когда они будут вызваны и будут ли вызваны вообще. Да и порядок вызова финализаторов не определен, поэтому при вызове финализатора некоторые «части» вашего объекта уже могут быть «разрушены», поскольку их финализаторы уже были вызваны. В общем, финализаторы – они-то есть, но это скорее «страховочный трос», а не нормальное средство управления ресурсами.&lt;/p&gt; &lt;a name='more'&gt;&lt;/a&gt;  &lt;h4&gt;Идиома RAII&lt;/h4&gt;  &lt;p align="justify"&gt;В языке С++, в котором нет никаких встроенных средств для автоматического управления памятью помимо умных указателей, уже давно активно применяется паттерн (или идиома) для своевременного освобождения ресурсов (*). Эта идиома носит название «Захват ресурса есть инициализация» (RAII - Resource Acquisition Is Initialization) и заключается в следующем. Ресурс захватывается в конструкторе и освобождается в деструкторе, а поскольку деструкторы вызываются автоматически, то и дополнительных усилий по управлению ресурсами больше не требуется.&lt;/p&gt;  &lt;p align="justify"&gt;Не удивительно, что эта же идея детерминированного управления ресурсами перекачивала и в другие более «умные» и «управляемые» среды, такие как .NET или Java (**) в виде интерфейса &lt;b&gt;IDisposable&lt;/b&gt; (в языке C#) и метода &lt;b&gt;dispose&lt;/b&gt; (в Java). Но, поскольку эти среды более умные, по сравнению со старичком С++, и основные проблемы, связанные с управлением памятью, в них решены, то переехала эта идиома не слишком хорошо. Нет, поймите меня правильно, переехала она вполне успешно, но для этого вам нужно использовать блок &lt;b&gt;using&lt;/b&gt; (для языка C#) или &lt;b&gt;try&lt;/b&gt;&lt;b&gt;-&lt;/b&gt;&lt;b&gt;with&lt;/b&gt;&lt;b&gt;-&lt;/b&gt;&lt;b&gt;resources&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;statement&lt;/b&gt; (в Java 7), если же вы «забудете» ими воспользоваться, то от вашего детерминированного освобождения ресурсов не останется и следа.&lt;/p&gt;  &lt;pre style="font-family: consolas"&gt;&lt;span style="color: green"&gt;// Открываем файл внутри блока using&lt;/span&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;using&lt;/span&gt; (&lt;span style="color: #2b91af"&gt;FileStream&lt;/span&gt; file = &lt;span style="color: #2b91af"&gt;File&lt;/span&gt;.OpenRead(&lt;span style="color: #a31515"&gt;&amp;quot;foo.txt&amp;quot;&lt;/span&gt;))&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// Выходим из функции при выполнении некоторого условия&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;if&lt;/span&gt; (someCondition) &lt;span style="color: blue"&gt;return&lt;/span&gt;;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// Файл будет закрыт автоматически при выходе из блока using&lt;/span&gt;&lt;br /&gt;}&lt;br /&gt;&lt;span style="color: green"&gt;// А что, если кто-то откроет файла вне блока using?&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #2b91af"&gt;FileStream&lt;/span&gt; file2 = &lt;span style="color: #2b91af"&gt;File&lt;/span&gt;.OpenRead(&lt;span style="color: #a31515"&gt;&amp;quot;foo.txt&amp;quot;&lt;/span&gt;);&lt;/pre&gt;

&lt;p align="justify"&gt;Однако это не единственная сложность, которая возникает при работе с ресурсами в .NET. Как мы вскоре увидим, использование обычного метода для освобождения ресурсов обладает и некоторыми другими проблемами. Поскольку метод &lt;b&gt;Dispose&lt;/b&gt; освобождает ресурсы, то вызов финализатора уже не нужен и его нужно отменить, кроме того, метод &lt;b&gt;Dispose&lt;/b&gt; разрушает инвариант класса, что дает пользователю возможность получить разрушенный или частично разрушенный объект. А это требует дополнительных проверок как в методе &lt;b&gt;Dispose&lt;/b&gt;, так и во всех публичных методах класса. &lt;/p&gt;

&lt;p align="justify"&gt;Все это привело к тому, что относительно простая идиома RAII вылилась на платформе .NET в паттерн, который так и называется “Dispose паттерн”. Однако прежде чем переходить к его рассмотрению, давайте рассмотрим два типа ресурсов, существующие на платформе .NET: управляемые и неуправляемые ресурсы.&lt;/p&gt;

&lt;h4&gt;Управляемые и неуправляемые ресурсы&lt;/h4&gt;

&lt;p align="justify"&gt;В .NET существует два типа ресурсов: управляемые и неуправляемые. Причем отличить их довольно просто: к неуправляемым ресурсам относятся только «сырые» ресурсы, типа &lt;b&gt;IntPtr&lt;/b&gt;, сырые дескрипторы сокетов или что-то в этом духе; если же с помощью идиомы RAII этот ресурс упаковали в объект, захватывающий его в конструкторе и освобождающий в методе &lt;b&gt;Dispose&lt;/b&gt;, то такой ресурс уже является управляемым. По сути, управляемые ресурсы – это «умные оболочки» для неуправляемых ресурсов, для освобождения которых не нужно вызывать какие-то хитроумные функции, а достаточно вызвать метод &lt;b&gt;Dispose&lt;/b&gt; интерфейса &lt;b&gt;IDisposable&lt;/b&gt;.&lt;/p&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;class&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;NativeResourceWrapper&lt;/span&gt; : &lt;span style="color: #2b91af"&gt;IDisposable&lt;/span&gt;&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// IntPtr – неуправляемый ресурс&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;private&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;IntPtr&lt;/span&gt; nativeResourceHandle;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;public&lt;/span&gt; NativeResourceWrapper()&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// «Захватываем» неуправляемый ресурс путем вызова специальной функции&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; nativeResourceHandle = AcquireNativeResource();&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;void&lt;/span&gt; Dispose()&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// Освобождаем захваченный ресурс, опять же, путем вызова какой-то специальной&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// функции&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; ReleaseNativeResource(nativeResourceHandle);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// Есть еще и финализатор, но его роль будет раскрыта позднее&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; ~NativeResourceWrapper() {...}&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;

&lt;p align="justify"&gt;Таким образом, любой объект может владеть ресурсами двух типов: он может непосредственно содержать неуправляемый ресурс (например&lt;b&gt;, &lt;/b&gt;&lt;b&gt;IntPtr&lt;/b&gt;) или же он может содержать ссылку на управляемый ресурс (например, &lt;b&gt;NativeResourceWrapper&lt;/b&gt;), при этом в обоих случаях объект, содержащий один из этих ресурсов, сам становится управляемым ресурсом. Это может показаться не слишком принципиальным, но понимать разницу между двумя типами ресурсов очень важно, поскольку работать с ними приходится по-разному.&lt;/p&gt;

&lt;h4&gt;Dispose pattern&lt;/h4&gt;

&lt;p align="justify"&gt;Итак, мы знаем, что объект может владеть двумя типами ресурсов: &lt;b&gt;управляемыми&lt;/b&gt; и &lt;b&gt;неуправляемыми&lt;/b&gt;; а также то, что у нас есть два способа освобождения ресурсов: &lt;b&gt;детерминированный&lt;/b&gt;, с помощью метода &lt;b&gt;Dispose&lt;/b&gt; и &lt;b&gt;недетерминированный&lt;/b&gt;, с помощью финализатора (***). А теперь давайте посмотрим, как со всем этим добром жить и, главное, как это добро освобождать.&lt;/p&gt;

&lt;p align="justify"&gt;Идея Dispose паттерна состоит в следующем: давайте мы всю логику освобождения ресурсов поместим в отдельный метод, и будем вызывать его и из метода &lt;b&gt;Dispose&lt;/b&gt;, и из финализатора, при этом, давайте добавим флажок, который бы говорил нам о том, кто вызвал этот метод. Поскольку эта простая идея содержит довольно большое количество подробностей, то давайте изложим Dispose паттерн по пунктам.&lt;/p&gt;

&lt;p align="justify"&gt;1. Класс, содержащий управляемые или неуправляемые ресурсы реализует интерфейс &lt;b&gt;IDisposable&lt;/b&gt;&lt;/p&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;class&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;Boo&lt;/span&gt; : &lt;span style="color: #2b91af"&gt;IDisposable&lt;/span&gt; {}&lt;br /&gt;&lt;/pre&gt;

&lt;p align="justify"&gt;2. Класс содержит метод &lt;b&gt;Dispose&lt;/b&gt;&lt;b&gt;(&lt;/b&gt;&lt;b&gt;bool&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;disposing&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;, который и делает всю работу по освобождению ресурсов; параметр &lt;b&gt;disposing&lt;/b&gt; говорит о том, вызывается ли этот метод из метода &lt;b&gt;Dispose&lt;/b&gt; или из финализатора. Этот метод должен быть &lt;b&gt;protected&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;virtual&lt;/b&gt; для не-sealed классов и &lt;b&gt;private&lt;/b&gt; для sealed классов&lt;/p&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: green"&gt;// Для не-sealed классов&lt;/span&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;protected&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;virtual&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;void&lt;/span&gt; Dispose(&lt;span style="color: blue"&gt;bool&lt;/span&gt; disposing) {} &lt;br /&gt;&lt;span style="color: green"&gt;// Для sealed классов&lt;/span&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;private&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;void&lt;/span&gt; Dispose(&lt;span style="color: blue"&gt;bool&lt;/span&gt; disposing) {} &lt;/pre&gt;

&lt;p align="justify"&gt;3. Метод Dispose всегда реализуется следующим образом: вначале вызывается метод &lt;b&gt;Dispose&lt;/b&gt;&lt;b&gt;(&lt;/b&gt;&lt;b&gt;true&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;, а затем может следовать вызов метода &lt;b&gt;GC&lt;/b&gt;&lt;b&gt;.&lt;/b&gt;&lt;b&gt;SuppressFinalize&lt;/b&gt;&lt;b&gt;()&lt;/b&gt;, который предотвращает вызов финализатора. &lt;/p&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;void&lt;/span&gt; Dispose()&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; Dispose(&lt;span style="color: blue"&gt;true&lt;/span&gt;&amp;#160;&lt;span style="color: green"&gt;/*called by user directly*/&lt;/span&gt;);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: #2b91af"&gt;GC&lt;/span&gt;.SuppressFinalize(&lt;span style="color: blue"&gt;this&lt;/span&gt;);&lt;br /&gt;}&lt;/pre&gt;

&lt;p align="justify"&gt;Метод &lt;b&gt;GC&lt;/b&gt;&lt;b&gt;.&lt;/b&gt;&lt;b&gt;SuppressFinalize&lt;/b&gt;&lt;b&gt;()&lt;/b&gt;, во-первых, должен вызываться после вызова &lt;b&gt;Dispose&lt;/b&gt;&lt;b&gt;(&lt;/b&gt;&lt;b&gt;true&lt;/b&gt;&lt;b&gt;)&lt;/b&gt;, а не перед ним, поскольку если метод &lt;b&gt;Dispose&lt;/b&gt;&lt;b&gt;(&lt;/b&gt;&lt;b&gt;true&lt;/b&gt;&lt;b&gt;)&lt;/b&gt; «упадет» с исключением, то выполнение финализатора не отменится. Во-вторых, &lt;b&gt;GC&lt;/b&gt;&lt;b&gt;.&lt;/b&gt;&lt;b&gt;SuppressFinalize&lt;/b&gt;&lt;b&gt;()&lt;/b&gt; должен вызываться даже для классов, не содержащих финализаторы, поскольку финализатор может появиться у его наследника (т.е. мы должны вызывать метод &lt;b&gt;GC&lt;/b&gt;&lt;b&gt;.&lt;/b&gt;&lt;b&gt;SuppressFinalize&lt;/b&gt;&lt;b&gt;()&lt;/b&gt; во всех не-sealed классах).&lt;/p&gt;

&lt;p align="justify"&gt;4. Метод &lt;b&gt;Dispose&lt;/b&gt;&lt;b&gt;(&lt;/b&gt;&lt;b&gt;bool&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;disposing&lt;/b&gt;&lt;b&gt;)&lt;/b&gt; содержит две части: (1) если этот метод вызван из метода &lt;b&gt;Dispose&lt;/b&gt; (т.е. параметр &lt;b&gt;disposing&lt;/b&gt; равен &lt;b&gt;true&lt;/b&gt;), то мы освобождаем управляемые и неуправляемые ресурсы и (2) если метод вызван из финализатора во время сборки мусора (параметр &lt;b&gt;disposing&lt;/b&gt; равен &lt;b&gt;false&lt;/b&gt;), то мы освобождаем только неуправляемые ресурсы.&lt;/p&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;void&lt;/span&gt; Dispose(&lt;span style="color: blue"&gt;bool&lt;/span&gt; disposing)&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;if&lt;/span&gt; (disposing)&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// Освобождаем только управляемые ресурсы&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// Освобождаем неуправляемые ресурсы&lt;/span&gt;&lt;br /&gt;}&lt;/pre&gt;

&lt;p align="justify"&gt;5. (ОПЦИОНАЛЬНО) Класс может содержать финализатор и вызывать из него &lt;b&gt;Dispose&lt;/b&gt;&lt;b&gt;(&lt;/b&gt;&lt;b&gt;bool&lt;/b&gt;&lt;b&gt; &lt;/b&gt;&lt;b&gt;disposing&lt;/b&gt;&lt;b&gt;)&lt;/b&gt; передавая &lt;b&gt;false&lt;/b&gt; в качестве параметра. &lt;/p&gt;

&lt;pre style="font-family: consolas"&gt;~Boo()&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; Dispose(&lt;span style="color: blue"&gt;false&lt;/span&gt;&amp;#160;&lt;span style="color: green"&gt;/*not called by user directly*/&lt;/span&gt;);&lt;br /&gt;}&lt;/pre&gt;

&lt;p align="justify"&gt;Не забывайте, что финализатор может быть вызван даже для частично сконструированных объектов, если конструктор этого класса сгенерирует исключение. Так что код очистки неуправляемых ресурсов должен учитывать то, что ресурсы еще не захвачены (****).&lt;/p&gt;

&lt;p align="justify"&gt;6. (ОПЦИОНАЛЬНО) Класс может содержать поле &lt;b&gt;bool&lt;/b&gt;&lt;b&gt; _&lt;/b&gt;&lt;b&gt;disposed&lt;/b&gt;, которое говорит о том, что ресурсы объекта уже освобождены. Disposable-классы должны спокойно позволять повторный вызов метода &lt;b&gt;Dispose&lt;/b&gt;, а также генерировать исключение при доступе к любым другим публичным методам или свойствам (поскольку инвариант объекта уже разрушен).&lt;/p&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;void&lt;/span&gt; Dispose(&lt;span style="color: blue"&gt;bool&lt;/span&gt; disposing)&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;if&lt;/span&gt; (disposed)&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;return&lt;/span&gt;; &lt;span style="color: green"&gt;// Ресурсы уже освобождены&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// Освобождаем ресурсы&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; disposed = &lt;span style="color: blue"&gt;true&lt;/span&gt;;&lt;br /&gt;}&lt;br /&gt; &lt;br /&gt;&lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;void&lt;/span&gt; SomeMethod()&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;if&lt;/span&gt; (disposed)&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;throw&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;new&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;ObjectDisposedException&lt;/span&gt;();&lt;br /&gt;}&lt;/pre&gt;

&lt;p align="justify"&gt;7. (ОПЦИОНАЛЬНО) Класс может наследовать от &lt;b&gt;CriticalFinalizerObject&lt;/b&gt;, если предыдущих шести пунктов мало и вы хотите большей экзотики. Наследование от этого класса дает вам дополнительные гарантии:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;div align="justify"&gt;Финализатор таких классов компилируется JIT-компилятором сразу при конструировании экземпляра, а не отложено по мере необходимости. Это дает возможность успешно выполниться финализатору даже в случае острой нехватки памяти.&lt;/div&gt;
  &lt;/li&gt;

  &lt;li&gt;
    &lt;div align="justify"&gt;Как мы уже говорили, CLR не гарантирует порядок вызова финализаторов, что делает невозможным обращение внутри финализатора к другим объектам, содержащим неуправляемые ресурсы. Однако CLR гарантирует что финализаторы «простых смертных» объектов будут вызваны &lt;b&gt;до&lt;/b&gt; наследников &lt;b&gt;CriticalFinalizerObject&lt;/b&gt;. Это дает возможность, в частности, из финализаторов ваших классов (если они не наследуют от &lt;b&gt;CriticalFinalizerObject&lt;/b&gt;) обращаться к полю &lt;b&gt;SafeHandle&lt;/b&gt;, которое точно будет освобождено позднее.&lt;/div&gt;
  &lt;/li&gt;

  &lt;li&gt;
    &lt;div align="justify"&gt;Финализаторы таких классов будут вызваны даже в случае экстренной выгрузки домена приложения.&lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: green"&gt;// А вы уверены, что оно вам нужно?&lt;/span&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;class&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;Foo&lt;/span&gt; : &lt;span style="color: #2b91af"&gt;CriticalFinalizerObject&lt;/span&gt; { }&lt;/pre&gt;

&lt;h4&gt;Прагматичный взгляд на Dispose паттерн&lt;/h4&gt;

&lt;p align="justify"&gt;Если вам показалось, что работа с ресурсами в .NET неоправдано сложна, то у меня по этому поводу есть две новости: одна хорошая, а другая – не очень. Новость «не очень» заключается в том, что работа с ресурсами даже сложнее, чем здесь описано (*****), хорошая же заключается в том, что в большинстве случаев вся эта сложность нас с вами касаться практически не будет.&lt;/p&gt;

&lt;p align="justify"&gt;Вся сложности реализации Dispose паттерна связаны с предположением о том, что один и тот же класс (или иерархия классов) может одновременно содержать как управляемые, так и неуправляемые ресурсы. Но давайте подумаем, а зачем вообще нам может понадобиться хранить неуправляемые ресурсы напрямую в классах бизнес-логики? А как же пресловутые &lt;b&gt;Принципы Единой Ответственности&lt;/b&gt; (SRP – Single Responsibility Principle) и &lt;b&gt;Здравого Смысла&lt;/b&gt;? Идиома RAII, описанная ранее, успешно используется десятки лет и предназначена как раз для таких случаев: &lt;b&gt;если у вас есть неуправляемый ресурс, то вместо того, чтобы работать с ним напрямую, оберните его в управляемую оболочку и работайте уже с нею&lt;/b&gt;.&lt;/p&gt;

&lt;p align="justify"&gt;Если посмотреть на .NET Framework, то можно заметить, что там используется именно такой подход: для всех ресурсов создается оболочка, которая прячет внутри всю сложность по работе с ресурсами, предоставляя пользователю лишь вызвать метод &lt;b&gt;Dispose&lt;/b&gt; для явной очистки ресурсов (ну, и финализатор, на всякий случай). Кроме того, для большей части неуправляемых ресурсов операционной системы такие оболочки уже сделаны, и изобретать велосипед не нужно.&lt;/p&gt;

&lt;p align="justify"&gt;Все это я веду к тому, что &lt;b&gt;не нужно смешивать в вашем коде бизнес-логику и логику по работе с неуправляемыми ресурсами&lt;/b&gt;. И то, и другое достаточно сложно само по себе и заслуживает отдельного класса. Вот и получается, что данный паттерн «оптимизирован» на очень редкий случай (что класс может содержать и управляемые и неуправляемые ресурсы), при этом делая наиболее распространенный случай, когда класс содержит только управляемые ресурсы, очень неудобной в реализации и сопровождении.&lt;/p&gt;

&lt;h4&gt;Упрощенная версия Dispose паттерна&lt;/h4&gt;

&lt;p align="justify"&gt;Если мы с вами знаем, что ни один человек не собирается смешивать управляемые и неуправляемые ресурсы в одном месте, так почему бы не реализовать это в коде явным образом? Мы можем оставить метод &lt;b&gt;Dispose&lt;/b&gt; и вместо дополнительного метода Dispose с совсем невнятным булевым параметром, добавить виртуальный метод &lt;b&gt;DisposeManagedResources&lt;/b&gt;, имя которого будет четко говорить о том, что мы должны освободить именно управляемые ресурсы. Модификатор доступа этого метода должен быть аналогичным нашему методу &lt;b&gt;Dispose(&lt;/b&gt;&lt;b&gt;bool)&lt;/b&gt;, т.е. &lt;b&gt;protected &lt;/b&gt;&lt;b&gt;virtual&lt;/b&gt; для не-sealed классов или &lt;b&gt;private&lt;/b&gt; для sealed классов.&lt;/p&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;class&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;SomethingWithManagedResources&lt;/span&gt; : &lt;span style="color: #2b91af"&gt;IDisposable&lt;/span&gt;&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;void&lt;/span&gt; Dispose()&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// Никаких Dispose(true) и никаких вызовов GC.SuppressFinalize()&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; DisposeManagedResources();&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// Никаких параметров, этот метод должен освобождать только неуправляемые ресурсы&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;protected&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;virtual&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;void&lt;/span&gt; DisposeManagedResources() {}&lt;br /&gt;}&lt;/pre&gt;

&lt;p align="justify"&gt;С первого взгляда такой подход может показаться слишком уж прагматичным, однако посудите сами: в книге &lt;a href="http://sergeyteplyakov.blogspot.com/2009/06/framework-design-guidelines-2nd-edition.html"&gt;Framework Design Guidelines&lt;/a&gt; описанию Dispose паттерна посвящено два десятка страниц, при этом ее авторы рекомендуют добавлять финализаторы только в случае острой необходимости. При этом все мы знаем, что смешивать два типа ресурсов в одном классе плохо, но все же следуем паттерну, который это поощряет, а не запрещает.&lt;/p&gt;

&lt;h4&gt;&lt;b&gt;Заключение&lt;/b&gt;&lt;/h4&gt;

&lt;p align="justify"&gt;При разработке библиотечных классов или бизнес-классов, которые будут использоваться в десятке других проектов, то следовать всем описанным выше принципам вполне разумно. К повторно используемому коду предъявляются другие требования и при их проектировании нужно следовать другим принципам: простота в использовании и расширяемость таких классов значительно важнее стоимости сопровождения. &lt;/p&gt;

&lt;p align="justify"&gt;Если же вы проектируете классы бизнес-логики или простые библиотеки с ограниченным кругом пользователей, то можно не морочить себе голову с «канонами», а использовать упрощенную версию этого паттерна, которая работает только с управляемыми ресурсами.&lt;/p&gt;

&lt;p&gt;------------------------------&lt;/p&gt;

&lt;p align="justify"&gt;(*) В С++, в отличие от C#, память тоже является ресурсом. Поэтому идиома RAII в языке С++ применяется как для освобождения динамически выделенной памяти, так и для освобождения любых других ресурсов, типа дескрипторов ОС или сокетов. &lt;/p&gt;

&lt;p align="justify"&gt;(**) В Java 7 наконец-то появилась конструкция, аналогичная конструкции &lt;b&gt;using&lt;/b&gt; языка C#: try-with-resource statement&lt;/p&gt;

&lt;p align="justify"&gt;(***) К сожалению в языке C# для финализаторов выбран тот же самый синтаксис (тильда, за которой идет имя класса), который используется для деструкторов в языке С++. Но семантика деструктора и финализатора очень разная, поскольку деструктор подразумевает детерминированное освобождение ресурсов, а финализатор – нет. &lt;/p&gt;

&lt;p align="justify"&gt;(***) Да, это еще одно отличие в поведении .NET и языка С++. В последнем, деструктор вызывается только для полностью сконструированного объекта, при этом вызываются деструкторы для всех полностью сконструированных его полей.&lt;/p&gt;

&lt;p align="justify"&gt;(****) Здесь я, например, не говорил о том, как можно получить «утечку ресурсов» при появлении исключений или о проблемах с изменяемыми значимыми типами, реализующими интерфейс &lt;b&gt;IDisposable&lt;/b&gt;. Об этом я уже писал ранее в заметках «&lt;a href="http://sergeyteplyakov.blogspot.com/2011/08/blog-post_15.html"&gt;Гарантии безопасности исключений&lt;/a&gt;» и «&lt;a href="http://sergeyteplyakov.blogspot.com/2011/07/blog-post.html"&gt;О вреде изменяемых значимых типов&lt;/a&gt;» соответственно.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8596733192274108952-4305682106128262073?l=sergeyteplyakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sergeyteplyakov.blogspot.com/feeds/4305682106128262073/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8596733192274108952&amp;postID=4305682106128262073' title='Комментарии: 12'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/4305682106128262073'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/4305682106128262073'/><link rel='alternate' type='text/html' href='http://sergeyteplyakov.blogspot.com/2011/09/dispose-pattern.html' title='Dispose pattern'/><author><name>Sergey Teplyakov</name><uri>https://profiles.google.com/108967431947412296254</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-2weHUILo1ns/AAAAAAAAAAI/AAAAAAAABRo/POab-anD6xA/s512-c/photo.jpg'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8596733192274108952.post-7757053690595343871</id><published>2011-09-19T21:53:00.001+03:00</published><updated>2011-09-20T08:46:42.789+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Анонсы'/><title type='text'>Google+</title><content type='html'>&lt;p align="justify"&gt;Каждый блогописец или блоговод, не знаю, как назвать сее существо правильно, сталкивается со следующим вопросом: вот, дескать, появилась у него светлая мысль и хочется этой мыслью поделится с миром. При этом возникает дилемма: то ли сваять все побыстренькому, не заморачиваясь особенно с темой и ее раскрытием или же покумекать хорошенько и выдать контент более осмысленный.&lt;/p&gt;  &lt;p align="justify"&gt;Я всегда склонялся ко второму варианту и в большинстве случаев именно этому принципу и следовал при ведении этого блога. Это выливается в то, что посты в этом блоге более или менее походят на небольшие (а иногда и на вполне увесистые) статейки и практически никогда не умещаются на одной странице.&lt;/p&gt;  &lt;p align="justify"&gt;Такое отношение к блогописательству я считаю вполне разумным и продолжать хочу именно в том же духе. Но иногда, по мере брождения (или брожения, не знаю, как правильно) по просторам этих ваших интернетов, сталкиваешься с чем-то айтишно-интересным, или, читая какую-нибудь интересную книжонку, находишь некоторую мысль весьма интересной, что ею хочется поделиться. При этом оформлять это дело в статью обычно нет ни сил, ни желания. Постить же в этот блог неоформленные мысли и простые ссылки не хочется категорически.&lt;/p&gt;  &lt;p align="justify"&gt;Посему такую “нестатейную” нишу я заполняю с помощью &lt;a href="https://plus.google.com/108967431947412296254/posts"&gt;гуглоплюса&lt;/a&gt; и &lt;a href="https://plus.google.com/108967431947412296254/buzz"&gt;живой ленты&lt;/a&gt; (&lt;a href="https://twitter.com/#!/STeplyakov"&gt;твиттер&lt;/a&gt; как-то мне не удобен для этих целей, хотя им я тоже иногда пользуюсь). Посему, если ты, дорогой мой читатель, интересуешься не только моими статьями, но и не столь полноценно оформленными мыслями, то милости прошу в упомянутые выше &lt;a href="https://plus.google.com/108967431947412296254/posts"&gt;гуглоплюс&lt;/a&gt; или &lt;a href="https://plus.google.com/108967431947412296254/buzz"&gt;живую ленту&lt;/a&gt;.&lt;/p&gt;  &lt;p align="justify"&gt;Сразу оговорюсь, что я совсем не фанат социальных сетей и не собираюсь постить всякую хрень, которой полным полно в интернете. В основном заметки в гуглоплюсе посвящены программированию и это всякие мысли, собственные наблюдения, размышления и т.п. Вот несколько последних постов для затравки:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;     &lt;div align="justify"&gt;&lt;a href="https://plus.google.com/108967431947412296254/posts/gPFYWtwqoQd?hl=ru"&gt;F#: непонятнки с литералами и паттерн-матчингом&lt;/a&gt;&lt;/div&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;div align="justify"&gt;&lt;a href="https://plus.google.com/108967431947412296254/posts/9WBeQh9XZoY?hl=ru"&gt;Profile Guided Optimization для С++ных проектов начиная с 2005-й студии&lt;/a&gt;&lt;/div&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;div align="justify"&gt;&lt;a href="https://plus.google.com/108967431947412296254/posts/DSuEi1gjCwe?hl=ru"&gt;О выступлении Гебра Саттера на BUILD конференции, посвященной современному С++&lt;/a&gt;&lt;/div&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;div align="justify"&gt;&lt;a href="https://plus.google.com/108967431947412296254/posts/1PTS9C1mQmH"&gt;Впечатления от проведения очередного семинара по асинхронному программированию&lt;/a&gt;&lt;/div&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;div align="justify"&gt;&lt;a href="https://plus.google.com/108967431947412296254/posts/MBCroGJcrty"&gt;О ковариантности спецификации исключений в языке С++&lt;/a&gt;&lt;/div&gt;   &lt;/li&gt; &lt;/ul&gt;  &lt;p align="justify"&gt;P.S. Ну, а кто на гуглоплюсе еще не зарегистрирован и нужен инвайт (там, кажется, он все еще нужен) – пишите, вышлю.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8596733192274108952-7757053690595343871?l=sergeyteplyakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sergeyteplyakov.blogspot.com/feeds/7757053690595343871/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8596733192274108952&amp;postID=7757053690595343871' title='Комментарии: 4'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/7757053690595343871'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/7757053690595343871'/><link rel='alternate' type='text/html' href='http://sergeyteplyakov.blogspot.com/2011/09/google.html' title='Google+'/><author><name>Sergey Teplyakov</name><uri>https://profiles.google.com/108967431947412296254</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-2weHUILo1ns/AAAAAAAAAAI/AAAAAAAABRo/POab-anD6xA/s512-c/photo.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8596733192274108952.post-6770826622831074701</id><published>2011-09-13T22:50:00.001+03:00</published><updated>2011-09-13T23:17:18.946+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Обработка исключений'/><category scheme='http://www.blogger.com/atom/ns#' term='Принципы разработки'/><title type='text'>Принцип самурая</title><content type='html'>&lt;p align="justify"&gt;В мире разработки софта существует много идей и «метафор», позаимствованных из других, казалось бы, не сильно связанных с программированием областей. Можно вспомнить паттерны проектирования, позаимствованные у архитекторов, или понятие «технического долга», пришедшее из финансовой индустрии, да и «эффектом второй системы» страдают проектировщики любых систем, а не только программных (*). Все это упрощает коммуникацию между разработчиками или между разработчиками и заказчиками, а также упрощает понимание той или иной проблемы в разработке ПО.&lt;/p&gt;  &lt;p align="justify"&gt;Еще одной метафорой, или скорее принципом разработки, является «&lt;b&gt;принцип самурая&lt;/b&gt;», призванный описать «контракт» между функцией и вызывающим ее кодом и заключается в следующем. Любая функция, реализующая некоторую единицу работы должна следовать тому же кодексу чести «бусидо», по которому живет любой самурай. Так, самурай не будет выполнять никаких заданий, противоречащих его «кодексу чести» и если к нему подойти с «непристойным» предложением, то он снесет вам башку раньше, чем вы успеете глазом моргнуть. Но если уж самурай возьмется за дело, то можно быть уверенным в том, что он доведет его до конца (**). &lt;/p&gt; &lt;a name='more'&gt;&lt;/a&gt;  &lt;p&gt;&lt;a href="http://lh4.ggpht.com/-xpzC4rwHxA8/Tm-0CPsUcpI/AAAAAAAABTM/O-lXo-uhT4E/s1600-h/clip_image002%25255B3%25255D.jpg"&gt;&lt;img style="border-right-width: 0px; display: block; float: none; border-top-width: 0px; border-bottom-width: 0px; margin-left: auto; border-left-width: 0px; margin-right: auto" title="clip_image002" border="0" alt="clip_image002" src="http://lh5.ggpht.com/-VLDDePEbq6s/Tm-0C7VKtMI/AAAAAAAABTQ/3rZ9iE03DcI/clip_image002_thumb.jpg?imgmax=800" width="336" height="453" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p align="justify"&gt;Тем же принципам должна следовать и любая &lt;b&gt;открытая&lt;/b&gt; функция. Если ей «подсунули» неадекватные входные данные, нарушающие ее предусловие (то есть ее кодекс чести), то она должна четко сказать об этом с помощью исключения (это аналог сноса башки, ведь вы сами в этом виноваты). Но если уж аргументы валидны и она вызывается в корректном состоянии, то вызывающий код может быть уверенным в результате: функция либо завершится успешно, либо «упадет». Функция, как и самурай должны следовать принципу «сделай или умри», но если самурай, чтобы избежать позора, делает себе сэппуку, то функция, если она не в состоянии выполнить свою работу, должна «упасть» с исключением.&lt;/p&gt;  &lt;p align="justify"&gt;Этот, казалось бы, нехитрый принцип дает ответы на многие непростые вопросы обработки исключений. Нужно ли проверять аргументы функции и что делать, если они некорректны? Нужно ли глотать исключения, которые происходят во внутренностях этой функции? Нужно ли возвращать &lt;b&gt;null&lt;/b&gt; или пустой список, если что-то пошло не так и функция не может выполнить свою работу? (***)&lt;/p&gt;  &lt;p align="justify"&gt;Если функции переданы неверные входные параметры или она вызывается в некорректном состоянии – генерируйте исключение; если при выполнении своей работы внутренняя функция генерирует исключение, то это означает, что и ваша функция свою работу выполнить не сможет. &lt;b&gt;В этом случае нужно либо пробрасывать исключение, либо завернуть его в другое исключение.&lt;/b&gt;&lt;/p&gt;  &lt;p align="justify"&gt;Из этого принципа следует, что «глотать» исключения – это плохо, поскольку вы, фактически, скрываете свои проблемы от глаз вызывающего кода и не даете внешнему коду возможности узнать об этом. Не нужно брать на себя слишком многого, пусть голова болит у вашего «клиента», что делать с «вашим телом» (то есть с исключением), когда вы не справились со своей задачей и решили последовать принципам самурая.&lt;/p&gt;  &lt;p align="justify"&gt;Возвращение null object-а в случае возникновения исключений также является опасной практикой, поскольку вызывающий код просто не сможет определить, является ли пустой объект корректным значением, или же при его получении произошла ошибка: &lt;/p&gt;  &lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;SomeEntry&lt;/span&gt; ReadEntryById(&lt;span style="color: blue"&gt;int&lt;/span&gt; id)&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;try&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// Читаем SomeEntry из базы данных&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;catch&lt;/span&gt; (&lt;span style="color: #2b91af"&gt;Exception&lt;/span&gt;)&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// Ядрёна кочарыжка! Как же вызывающему коду узнать,&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// была ли ошибка, или записи с таким id нет в базе?&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;return&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;null&lt;/span&gt;;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;}&lt;/pre&gt;

&lt;p align="justify"&gt;Бывают случаи, когда функция может перехватывать исключение и не пробрасывать его вызывающему коду; это может быть функция самого высокого уровня и вызывающего кода может не быть. В остальных же случаях, функция может самостоятельно попытаться восстановиться после возникновения ошибки, но, в конечном счете, если эта попытка не увенчается успехом, то у нее не останется другого выхода, кроме как последовать принципу самурая – «сделать свою работу или умереть».&lt;/p&gt;

&lt;p&gt;--------------&lt;/p&gt;

&lt;p align="justify"&gt;(*) За подробностями о том, что это за метафоры, обращайтесь к соответствующим заметкам: &lt;a href="http://sergeyteplyakov.blogspot.com/2011/05/blog-post.html"&gt;«Технический долг»&lt;/a&gt; и &lt;a href="http://sergeyteplyakov.blogspot.com/2011/06/blog-post.html"&gt;«Эффект второй системы»&lt;/a&gt;, соответственно.&lt;/p&gt;

&lt;p align="justify"&gt;(**) В большинстве статей о «принципе самурая» говорится только о второй части соглашения, что, дескать, любой метод, как и истинный самурай, должны либо выполнить свою работу, либо умереть. Но вызывающий код не является начальником самурая или его императором, которого самурай должен слушаться беспрекословно. Самурай, как и функция, не должны выполнять задания, противоречащие их «кодексу чести»; в отношениях между функцией и вызывающим кодом важно, чтобы обе стороны выполняли свои соглашения. &lt;/p&gt;

&lt;p align="justify"&gt;(***) Принцип самурая не является революцией в разработке ПО; этому принципы следуют уже давно, причем некоторые делают это достаточно формальным образом с помощью проектирования по контракту. Если вы хотите познакомиться с понятиями предусловия и постусловия, то можно начать со статьи &lt;a href="http://sergeyteplyakov.blogspot.com/2011/04/blog-post.html"&gt;«Как не надо писать код»&lt;/a&gt;, или же обратиться к целой серии статей, посвященных теме &lt;a href="http://sergeyteplyakov.blogspot.com/search/label/Design%20by%20Contract"&gt;проектирования по контракту&lt;/a&gt;.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8596733192274108952-6770826622831074701?l=sergeyteplyakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sergeyteplyakov.blogspot.com/feeds/6770826622831074701/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8596733192274108952&amp;postID=6770826622831074701' title='Комментарии: 7'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/6770826622831074701'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/6770826622831074701'/><link rel='alternate' type='text/html' href='http://sergeyteplyakov.blogspot.com/2011/09/blog-post_13.html' title='Принцип самурая'/><author><name>Sergey Teplyakov</name><uri>https://profiles.google.com/108967431947412296254</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-2weHUILo1ns/AAAAAAAAAAI/AAAAAAAABRo/POab-anD6xA/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh5.ggpht.com/-VLDDePEbq6s/Tm-0C7VKtMI/AAAAAAAABTQ/3rZ9iE03DcI/s72-c/clip_image002_thumb.jpg?imgmax=800' height='72' width='72'/><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8596733192274108952.post-898316273638748297</id><published>2011-09-05T21:35:00.001+03:00</published><updated>2011-09-05T21:35:50.007+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Книги'/><title type='text'>Вы, конечно, шутите, мистер Фейнман</title><content type='html'>&lt;p align="justify"&gt;&lt;a href="http://lh6.ggpht.com/-Oi75g1bHk44/TmUWgypRWRI/AAAAAAAABTA/8gjilwvnI50/s1600-h/feynman_schytite_317_449_1%25255B2%25255D.png"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; margin-left: 0px; border-left-width: 0px; margin-right: 0px" title="feynman_schytite_317_449_1" border="0" alt="feynman_schytite_317_449_1" align="left" src="http://lh3.ggpht.com/-zDpBk5xTV-s/TmUWhRFYNXI/AAAAAAAABTE/1134PJ3RcKk/feynman_schytite_317_449_1_thumb%25255B2%25255D.jpg?imgmax=800" width="187" height="287" /&gt;&lt;/a&gt; &lt;/p&gt;  &lt;p align="justify"&gt;Мое знакомство с мистером Фейнманом началось с &lt;a href="http://sergeyteplyakov.blogspot.com/2010/03/blog-post_25.html"&gt;перевода&lt;/a&gt; статьи Эрика Липперта &lt;a href="http://blogs.msdn.com/b/ruericlippert/archive/2011/02/28/what-would-feynman-do.aspx"&gt;«Что бы сделал мистер Фейнман»&lt;/a&gt;. В этой замечательной заметке в виде шуточного рассказа приведены ответы на «нестандартные вопросы», так любимые некоторыми «мелкомягкими» компаниями при отборе кандидатов на работу. А поскольку мистер Фейнман всегда любил «нестандартные вопросы» и давал на них еще менее «стандартные ответы», то интервью получилось веселым и, как я узнал позднее, очень похожим на неопубликованную главу из книги «Вы, конечно, шутите, мистер Фейнман».&lt;/p&gt;   &lt;p align="justify"&gt;Сама же книга представляет собой сборник забавных и трогательных историй о жизни «простого» человека, по имени Ричард Филлипс Фейнман, самым незначительным достижением которого (по его собственному разумению) было получение Нобелевской премии по физике. Это книга о замечательном человеке, умном, талантливом, упрямом; человеке, у которого жажда познаний, экспериментов и решения разных головоломок была практически безграничной. &lt;/p&gt; &lt;a name='more'&gt;&lt;/a&gt; &lt;p align="justify"&gt;&lt;b&gt;&lt;i&gt;Цитата&lt;/i&gt;&lt;/b&gt;&lt;i&gt;: Мне интересно, почему. Мне интересно, почему. Мне интересно, почему мне интересно. Мне интересно, почему мне интересно, почему Мне интересно, почему мне интересно?&lt;/i&gt;&lt;/p&gt;  &lt;p align="justify"&gt;Будучи еще двенадцатилетним ребенком, этот парень смастерил в своей «лаборатории» простенькую охранную сигнализацию, он сделал щиток управления для собственного стенда, на котором была лампочка, которая загоралась, в случае неисправностей; он чинил радиоприемники всей округе «думая». Понятно, что в тридцатых годах прошлого века радиоприемники были весьма простыми устройствами, но, тем не менее, сколько ребят в этом возрасте могут сделать свой собственный предохранитель или сделать вольтметр из амперметра?&lt;/p&gt;  &lt;p align="justify"&gt;&lt;b&gt;&lt;i&gt;Цитата&lt;/i&gt;&lt;/b&gt;&lt;i&gt;: Я плохо представляю, что происходит с людьми: они не учатся путем понимания. Они учатся каким-то другим способом – путем механического запоминания или как-то иначе. Их знания так хрупки!&lt;/i&gt;&lt;/p&gt;  &lt;p align="justify"&gt;В этой книге нет четкого сюжета, нет особой темы и идеи, как нет их и в нашей жизни. Мы живем, растем, учимся, любим, увлекаемся чем-то; и эта книга, будучи автобиографической, построена подобным образом. Но есть одна тема, к которой Фейнман возвращается снова и снова: это темы образования, познания, науки. Он постоянно пишет о том, что люди должны учиться путем понимания, а не механического запоминания, и что учебники должны этому способствовать. Поскольку наука направлена на понимание законов природы, то в учебниках должны быть примеры, связывающие научные законы с окружающим миром, чтобы школьник или студент не просто «зазубрил» определение, а понял, что за ним стоит, и смог бы потом проверить это самостоятельно на практике. Будучи еще учеником старших классов, Ричард придумывал интересные задачи по геометрии, поскольку искренне считал, что примеры в существующих учебниках являются унылыми. Все это, в конечном счете, привело к появлению знаменитых «Фейнмановских лекций по физике», которые вот уже более 50 лет считаются одним из лучших курсов по физике.&lt;/p&gt;  &lt;p align="justify"&gt;&lt;b&gt;&lt;i&gt;Цитата&lt;/i&gt;&lt;/b&gt;&lt;i&gt;: Однажды я начал возиться с вычислительной машинкой и заметил нечто очень своеобразное. Если взять единичку и разделить на 243, то получится 0,004115226337448… Любопытно… Я решил, что это довольно забавно.&lt;/i&gt;&lt;/p&gt;  &lt;p align="justify"&gt;Пару лет назад, задолго до прочтения этой книги, я листал замечательную книгу Джона Бентли «Жемчужины программирования» (**). Тогда мое внимание привлекла седьмая глава под названием «Предварительные оценки», в которой автор предлагает проверить свою «интуицию» на некоторых вопросах, а затем показывает пользу «предварительных оценок» для разработчика. После этого, практически в самом конце главы приводится следующая цитата:&lt;/p&gt;  &lt;p align="justify"&gt;&lt;i&gt;&amp;quot;Мне частенько приходилось слышать, что предварительные оценки называли &amp;quot;приближенными оценками Ферми&amp;quot; по имени знаменитого физика. История гласит, что Энрико Ферми, Роберт Оппенгеймер и другие члены Манхэттенского проекта ждали взрыва первой атомной бомбы за низкой стеной в нескольких километрах от эпицентра. Ферми нарвал множество маленьких листочков бумаги, которые он подбросил в воздух, увидев вспышку взрыва. Когда ударная волна миновала их, он измерил расстояние, на которое были отброшены бумажки, и быстро рассчитал мощность взрыва атомной бомбы, которая была позже точно подтверждена дорогостоящим измерительным оборудованием&amp;quot;&lt;/i&gt;.&lt;/p&gt;  &lt;p align="justify"&gt;Я благополучно забыл, чьим именем называли предварительные оценки, да и вообще я долгое время не мог вспомнить, где я о них прочитал. Читая книгу о «мистере Фейнмане» у меня было постоянное ощущение дежавю, настолько часто и умело Фейнман пользовался своим умением сказать, «как приблизительно будет выглядеть ответ, или, когда ответ получен, - объяснить, почему он именно таков». Да, в указанной выше цитате главным героем не мог быть Фейнман, поскольку он в это время смотрел на ядерный взрыв через ветровое стекло грузовика, ведь он знал, что глаза могут пострадать не от яркой вспышки, а именно от ультрафиолета, который благополучно будет поглощен этим стеклом (***). &lt;/p&gt;  &lt;p align="justify"&gt;&lt;b&gt;&lt;i&gt;Цитата&lt;/i&gt;&lt;/b&gt;&lt;i&gt;: - А где ты его взял?      &lt;br /&gt;&lt;/i&gt;&lt;i&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; - У тебя в шкафу.      &lt;br /&gt;&lt;/i&gt;&lt;i&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; - Но я запер его!      &lt;br /&gt;&lt;/i&gt;&lt;i&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; - Знаю, что ты его запер. Но замки – барахло!&lt;/i&gt;&lt;/p&gt;  &lt;p align="justify"&gt;Эту книгу интересно читать не только потому, что она легко написана, в ней много юмора и интересных историй. Ее интересно читать еще и потому, что из нее можно почерпнуть немало интересного для себя. В книге показан отличный пример того, что зацикливаться на чем-то одном в своей жизни совершенно не обязательно. Помимо того, что «мистер Фейнман» был прекрасным физиком, он профессионально рисовал и играл на барабанах и фригидейре; он изучал португальский и японский; он взламывал сейфы и изучал письмена Майа, оспаривая мнение мировых экспертов в этой области; а его экспериментам с муравьями позавидовал бы Бернард Вербер (****). Одним словом, Ричард Фейнман является отличным примером того, что человек может быть талантливым в самых разных областях. А нужно для этого, всего ничего: страстное желание и «немного» усердия.&lt;/p&gt;  &lt;p align="justify"&gt;------------------------------&lt;/p&gt;  &lt;p align="justify"&gt;(*) Конечно же, вся эта статья является чистейшей воды выдумкой, но вот атмосфера и стиль повествования очень похожи. Не зря, эта заметка Эрика, несмотря на свой нетехнический характер и отсутствие малейшей информации о языке C# и компиляторах, оказалась такой популярной.&lt;/p&gt;  &lt;p align="justify"&gt;(**) Рецензия на книгу Джона Бентли в моем &lt;a href="http://sergeyteplyakov.blogspot.com/search/label/%D0%9A%D0%BD%D0%B8%D0%B3%D0%B8"&gt;списке рецензий&lt;/a&gt; отсутствует, но небольшое мнение о ней можно узнать в статье &lt;a href="http://sergeyteplyakov.blogspot.com/2010/03/blog-post.html"&gt;«Классические книги по программированию»&lt;/a&gt;.&lt;/p&gt;  &lt;p align="justify"&gt;(***) Если честно, то я сомневаюсь в правдивости цитаты, приведенной Джоном Бентли по одной простой причине: если бы это было действительно так, то Фейнман, также будучи участником Манхэттенского проекта, об этом наверняка бы знал, и обязательно бы упомянул в своих «записках». Но с чем действительно не поспоришь, так это с тем, что Ферми был величайшим мастером предварительным оценок, и даже превосходил в своем мастерстве Фейнмана, о чем последний неоднократно упоминает.&lt;/p&gt;  &lt;p align="justify"&gt;(****) Речь идет о популярном современном писателе, авторе знаменитого трехтомника &lt;a href="http://ru.wikipedia.org/wiki/%D0%9C%D1%83%D1%80%D0%B0%D0%B2%D1%8C%D0%B8_(%D1%80%D0%BE%D0%BC%D0%B0%D0%BD)"&gt;«Муравьи»&lt;/a&gt;.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8596733192274108952-898316273638748297?l=sergeyteplyakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sergeyteplyakov.blogspot.com/feeds/898316273638748297/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8596733192274108952&amp;postID=898316273638748297' title='Комментарии: 8'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/898316273638748297'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/898316273638748297'/><link rel='alternate' type='text/html' href='http://sergeyteplyakov.blogspot.com/2011/09/blog-post.html' title='Вы, конечно, шутите, мистер Фейнман'/><author><name>Sergey Teplyakov</name><uri>https://profiles.google.com/108967431947412296254</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-2weHUILo1ns/AAAAAAAAAAI/AAAAAAAABRo/POab-anD6xA/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh3.ggpht.com/-zDpBk5xTV-s/TmUWhRFYNXI/AAAAAAAABTE/1134PJ3RcKk/s72-c/feynman_schytite_317_449_1_thumb%25255B2%25255D.jpg?imgmax=800' height='72' width='72'/><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8596733192274108952.post-5733640038519051923</id><published>2011-08-29T23:05:00.001+03:00</published><updated>2011-08-29T23:22:23.966+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='Переводы'/><title type='text'>Wanted! Старые хиты Эрика Липперта</title><content type='html'>&lt;p align="justify"&gt;Если вы, вдруг не знали, то помимо всякой ерунды, публикуемой на этом блоге, я еще и занимаюсь переводом блога Эрика Липперта (Eric Lippert) &lt;a href="http://blogs.msdn.com/b/ericlippert/"&gt;Fabulous Adventures in Coding&lt;/a&gt; на русский язык. В русскоязычном варианте этот блог носит название &lt;a href="http://blogs.msdn.com/b/ruericlippert/"&gt;Невероятные приключения в коде&lt;/a&gt;.&lt;/p&gt;  &lt;p align="justify"&gt;Занимаюсь я этим делом вот уже &lt;a href="http://sergeyteplyakov.blogspot.com/2010/03/blog-post_25.html"&gt;полтора года&lt;/a&gt; и не разу не пожалел потраченного времени. Я надеюсь, что чтение статей Эрика на русском языке доставляет хоть малую толику того удовольствия, которое я получаю при переводе!&lt;/p&gt;  &lt;p align="justify"&gt;Но сегодня я не об этом. Точнее не совсем об этом. Мы начали публиковать переводы, датированные апрелем 2009-го года, но, как это ни удивительно, до этого Эрик писал не менее часто и не менее интересно. Посему я предлагаю вернуться к его старым хитам и восстановить, так сказать, справедливость и перевести на русский язык и их.&lt;/p&gt;  &lt;div align="justify"&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;/div&gt;  &lt;p align="justify"&gt;Я сделал небольшой список того, что я думаю перевести, но, вполне возможно, что я что-то упустил (а я точно что-то упустил), поэтому я всецело открыт для предложений (*). Итак, вот сам список:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;     &lt;div align="justify"&gt;&lt;a href="http://blogs.msdn.com/b/ericlippert/archive/2009/04/27/the-stack-is-an-implementation-detail.aspx"&gt;The Stack Is An Implementation Detail, Part One&lt;/a&gt; (2009-04-27)         &lt;br /&gt;&lt;a href="http://blogs.msdn.com/b/ericlippert/archive/2009/05/04/the-stack-is-an-implementation-detail-part-two.aspx"&gt;The Stack Is An Implementation Detail, Part Two&lt;/a&gt; (2009-04-05)         &lt;br /&gt;Пара весьма интересных и популярных статей, в которых рассматривается разница между значимыми и ссылочными типами с точки зрения расположениях их экземпляров в памяти. Эта тема рассматривается, наверное, в сотне различных статей, да и сам Эрик пишет об этом весьма часто. Тем не менее, это одна из лучших заметок в этой теме.&lt;/div&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;div align="justify"&gt;&lt;a href="http://blogs.msdn.com/b/ericlippert/archive/2009/04/29/events-and-races.aspx"&gt;Events and Races&lt;/a&gt; (2009-04-29)        &lt;br /&gt;Одним из классических вопросов правильного “зажигания” событий является вопрос многопоточности. В этой заметке речь идет именно об этом.&lt;/div&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;div align="justify"&gt;&lt;a href="http://blogs.msdn.com/b/ericlippert/archive/2009/03/19/representation-and-identity.aspx"&gt;Representation and Identity&lt;/a&gt; (2009-03-19)        &lt;br /&gt;Отличная заметка о том, почему при распаковке упакованного байта в int мы получаем исключение &lt;strong&gt;InvalidCastException&lt;/strong&gt;.&lt;/div&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;div align="justify"&gt;&lt;a href="http://blogs.msdn.com/b/ericlippert/archive/2009/03/06/locks-and-exceptions-do-not-mix.aspx?PageIndex=3"&gt;Locks and exceptions do not mix&lt;/a&gt; (2009-03-06)         &lt;br /&gt;В статье рассматриваются изменения, внесенные в реализацию метода Monitor.Enter в .Net 4.0 для предотвращения возможных deadlock-ов, которые были вполне возможны в более ранних версиях .Net Framework.&lt;/div&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;div align="justify"&gt;&lt;a href="http://blogs.msdn.com/b/ericlippert/archive/2009/02/17/references-are-not-addresses.aspx"&gt;References are not addresses&lt;/a&gt; (2009-02-17)         &lt;br /&gt;Рассматривается разница между ссылками и указателями в языке C#, ну и, как говорит название, Эрик показывает, почему ссылки некорректно рассматривать, как адрес в памяти.&lt;/div&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;div align="justify"&gt;&lt;a href="http://blogs.msdn.com/b/ericlippert/archive/2009/01/26/why-no-var-on-fields.aspx"&gt;Why no var on fields?&lt;/a&gt; (2009-01-26)        &lt;br /&gt;И правда, почему? На часть этих вопросов я уже ответил совсем недавно &lt;a href="http://sergeyteplyakov.blogspot.com/2011/08/c.html"&gt;в своей заметке&lt;/a&gt;, ну а все сложности реализации компилятора лучше почитать у Эрика:)&lt;/div&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;div align="justify"&gt;&lt;a href="http://blogs.msdn.com/b/ericlippert/archive/2008/09/10/vexing-exceptions.aspx"&gt;Vexing exceptions&lt;/a&gt; (2008-09-10)         &lt;br /&gt;Очень полезная статья, в которой рассматриваются три «семантических» типа исключений. Все это помогает устаканить в голове некоторые вопросы обработки исключений и таки понять, как их обрабатывать.&lt;/div&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;div align="justify"&gt;&lt;a href="http://blogs.msdn.com/b/ericlippert/archive/2008/09/08/high-maintenance.aspx"&gt;High maintenance&lt;/a&gt; (2008-09-08)         &lt;br /&gt;Отличное ревью всего лишь одной функции, в 6-ти строках которой показаны 5 разных code smells. Много интересных мыслей, начиная от контракта функции заканчивая проблемами предварительного обобщения (premature generialization).&lt;/div&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;div align="justify"&gt;&lt;a href="http://blogs.msdn.com/b/ericlippert/archive/2008/05/16/reading-code-over-the-telephone.aspx"&gt;Reading Code Over the Telephone&lt;/a&gt; (2008-05-16)         &lt;br /&gt;Не rocket science, но, тем не менее, очень полезно знать, как читаются различные лямбда-выражение.&lt;/div&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;div align="justify"&gt;&lt;a href="http://blogs.msdn.com/b/ericlippert/archive/2007/08/17/subtleties-of-c-il-codegen.aspx"&gt;Subtleties of C# IL codegen&lt;/a&gt; (2007-08-17)         &lt;br /&gt;Интересная заметка, в которой показывается почему, например, lock(expression) statement не является потокобезопасными в случае возникновения асинхронных исключений, а также рассмотрена разница между инструкциями &lt;b&gt;call&lt;/b&gt; и &lt;b&gt;callvirt&lt;/b&gt;.&lt;/div&gt;   &lt;/li&gt; &lt;/ul&gt;  &lt;p align="justify"&gt;Еще раз напомню, что это не полный список, а лишь то, что было у меня в закладках, так что никакой претензии на полноту. А посему, жду ваших преложений!&lt;/p&gt;  &lt;p align="justify"&gt;----------------&lt;/p&gt;  &lt;p align="justify"&gt;(*) Я надеюсь на разумную аргументацию предложений. Мы просто не в состоянии перевести и опубликовать все старые посты, да и необходимости в этом, откровенно говоря, нет. Поэтому, если вы предлагаете к переводу некоторую статью, то разумно как минимум привести краткое содержание (парой предложений), ну и если возможно, то рассказать, чем же она такая выдающаяся.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8596733192274108952-5733640038519051923?l=sergeyteplyakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sergeyteplyakov.blogspot.com/feeds/5733640038519051923/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8596733192274108952&amp;postID=5733640038519051923' title='Комментарии: 6'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/5733640038519051923'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/5733640038519051923'/><link rel='alternate' type='text/html' href='http://sergeyteplyakov.blogspot.com/2011/08/wanted.html' title='Wanted! Старые хиты Эрика Липперта'/><author><name>Sergey Teplyakov</name><uri>https://profiles.google.com/108967431947412296254</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-2weHUILo1ns/AAAAAAAAAAI/AAAAAAAABRo/POab-anD6xA/s512-c/photo.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8596733192274108952.post-7603098555910846375</id><published>2011-08-25T21:41:00.001+03:00</published><updated>2011-08-25T21:44:13.023+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><title type='text'>Неявно типизированные поля в C#</title><content type='html'>&lt;p align="justify"&gt;Сегодня на &lt;a href="http://rsdn.ru"&gt;кывте&lt;/a&gt; был задан очередной весьма &lt;a href="http://rsdn.ru/forum/dotnet/4394093.flat.aspx"&gt;интересный вопрос&lt;/a&gt; о том, почему в языке C# существуют неявно типизированные локальные переменные (implicitely-typed local variables) a.k.a. &lt;b&gt;var&lt;/b&gt;, но нет неявно типизированных полей?&lt;/p&gt;  &lt;p align="justify"&gt;На самом деле, такое положение дел вовсе не случайно; так что давайте рассмотрим несколько причин, почему компилятор ведет себя именно так, а не иначе.&lt;/p&gt;  &lt;p align="justify"&gt;Во-первых, возможность использовать &lt;b&gt;var&lt;/b&gt;, для объявления неявно типизированных локальных переменных, никогда не была самостоятельной возможностью. При разработке языка C# никто не ставил перед собой цели создать полностью неявно типизированный язык программирования, типа F#; неявная типизация была лишь одной составляющей (пусть и немаловажной) более общей концепции, известной сегодня под аббревиатурой LINQ. &lt;/p&gt; &lt;a name='more'&gt;&lt;/a&gt;  &lt;p align="justify"&gt;Поскольку при разрабоке LINQ-а было принято решение, что завязывать разработчика только на существующие типы является слишком «злым» ограничением, то при ее реализации была предоставлена разработчику возможность возвращать последовательности анонимных классов. А раз так, то без использования неявно типизированных переменных это было бы не возможно:&lt;/p&gt;  &lt;pre style="font-family: consolas"&gt;&lt;span style="color: #2b91af"&gt;IEnumerable&lt;/span&gt;&amp;lt;&lt;span style="color: #2b91af"&gt;Customer&lt;/span&gt;&amp;gt; customers = &lt;span style="color: blue"&gt;null&lt;/span&gt;;&lt;br /&gt;&lt;span style="color: green"&gt;// Упс, а тип переменной result какой? IEnumerable&amp;lt;??&amp;gt;?&lt;/span&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;var&lt;/span&gt; result = &lt;span style="color: blue"&gt;from&lt;/span&gt; c &lt;span style="color: blue"&gt;in&lt;/span&gt; customers&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;where&lt;/span&gt; c.Age &amp;gt; 33&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;select&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;new&lt;/span&gt; { c.Name, c.Age };&lt;br /&gt;&lt;/pre&gt;

&lt;p align="justify"&gt;Однако использование ключевого слова &lt;b&gt;var&lt;/b&gt; в объявлении типа никак бы не помогло решить более глобальную цель (это я опять про LINQ). &lt;b&gt;&lt;/b&gt;&lt;/p&gt;

&lt;p align="justify"&gt;Во-вторых, даже если не залазить слишком глубоко в то, насколько сложно было бы реализовать такую возможность компиляторописателям (*), даже навскидку с ее реализацией и использованием связаны некоторые недостатки. Первый недостаток связан с тем, что неявно типизированные поля будут плохо дружить с анонимными типами.&lt;/p&gt;

&lt;p align="justify"&gt;Давайте представим себе следующий класс:&lt;/p&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;class&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;Foo&lt;/span&gt;&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;var&lt;/span&gt; someField = &lt;span style="color: blue"&gt;new&lt;/span&gt; {Name = &lt;span style="color: #a31515"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;, Value = 12};&lt;br /&gt;&lt;br /&gt;}&lt;/pre&gt;

&lt;p align="justify"&gt;Поскольку анонимные типы в .Net реализованы сейчас в виде internal-типов, то «экспортировать» этот тип за пределы текущей сборки просто напросто нельзя. Можно, конечно же, ограничить использование неявно типизированных полей только для интернал классов или приватных полей, или вообще запретить использование неявно типизированных полей с анонимными классами, но это сделает поведение двух семантически схожих конструкций языка C# слишком разным. Одна из причин появление &lt;b&gt;var&lt;/b&gt; в C# 3.0 – это использование анонимных типов (с LINQ-ом или без), а тут получается, что неявнотипизированные поля с ними работать не будут.&lt;/p&gt;

&lt;p align="justify"&gt;Еще одним важным ограничением является то, что неявно типизированные поля вводят слишком тесную связь между инициализируемым полем и «инициализатором». Давайте рассмотрим такой пример: предположим, у нас есть класс &lt;b&gt;A&lt;/b&gt;, который содержит “var”-поле с именем &lt;b&gt;foo&lt;/b&gt;, которое инициализируется путем вызова статического метода &lt;b&gt;Foo&lt;/b&gt; класса &lt;b&gt;B&lt;/b&gt;:&lt;/p&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;class&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;A&lt;/span&gt;&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;static&lt;/span&gt;&amp;#160;&lt;span style="color: red"&gt;&lt;font color="#0080ff"&gt;var&lt;/font&gt;&lt;/span&gt; foo = &lt;span style="color: #2b91af"&gt;B&lt;/span&gt;.Foo();&lt;br /&gt;}&lt;br /&gt; &lt;br /&gt;&lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;class&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;B&lt;/span&gt;&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;static&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;int&lt;/span&gt; Foo()&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;return&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;default&lt;/span&gt;(&lt;span style="color: blue"&gt;int&lt;/span&gt;);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;}&lt;/pre&gt;

&lt;p align="justify"&gt;Это приводит к целому ряду дополнительных вопросов: а что будет, если классы &lt;b&gt;A&lt;/b&gt; и &lt;b&gt;B&lt;/b&gt; расположены в разных сборках? А что если без перекомпиляции сборки &lt;b&gt;A&lt;/b&gt; будет перекомпилирована сборка с классом &lt;b&gt;B&lt;/b&gt; и тип возвращаемого значения метода &lt;b&gt;Foo&lt;/b&gt; изменится с типа &lt;b&gt;int&lt;/b&gt; на &lt;b&gt;string&lt;/b&gt;? Или же у нас может быть еще неявно типизированное поле &lt;b&gt;C.&lt;/b&gt;&lt;b&gt;foo&lt;/b&gt; (т.е. поле &lt;b&gt;foo&lt;/b&gt; в классе &lt;b&gt;С&lt;/b&gt;), завязанное на тип поля &lt;b&gt;A.&lt;/b&gt;&lt;b&gt;foo&lt;/b&gt;, поле &lt;b&gt;D.&lt;/b&gt;&lt;b&gt;foo&lt;/b&gt;, завязанное на поле &lt;b&gt;C.&lt;/b&gt;&lt;b&gt;foo&lt;/b&gt; и т.д. и тогда изменение типа возвращаемого значения одной функции приведет к изменению типов полей в десятке разных классов (**). Да и вообще, поля являются более важной частью дизайна класса и его реализации по сравнению с локальными переменными, поэтому изменение «на лету» типа этого поля, только из-за того кто-то поменял сигнатуру функции в третьем модуле является не лучшей идеей.&lt;/p&gt;

&lt;p align="justify"&gt;Конечно же, можно было бы ограничить возможность использования неявно типизированных полей лишь в ограниченном наборе случаев, и запрещать использование этой возможности с анонимными типами и классами из других сборок, а оставить, например, только использование методов текущего класса. Но даже в этом случае реализация этой возможности требует слишком больших усилий на реализацию (пруф у Эрика, я тут не причемJ), что делает ее довольно низкоприоритетной в бесконечном списке улучшений, которые есть в головах разработчиков компилятора языка C#.&lt;/p&gt;

&lt;p&gt;----------------------&lt;/p&gt;

&lt;p align="justify"&gt;(*) Залазить «в дебри» нет никакого смысла, поскольку это уже сделал Эрик Липперт в своей заметке &lt;a href="http://blogs.msdn.com/b/ericlippert/archive/2009/01/26/why-no-var-on-fields.aspx"&gt;Why no var on fields?&lt;/a&gt;, в которой он как раз и рассказывает о том, что реализация неявно типизированных полей потребовала бы значительно более существенных затрат на реализацию, нежели реализация неявно типизированных локальных переменных.&lt;/p&gt;

&lt;p align="justify"&gt;(**) Конечно, это не показатель, и любую возможность использовать правильно или не правильно. Но с помощью этой штуки можно нагородить такого, что пример, приведенный в заметке &lt;a href="http://sergeyteplyakov.blogspot.com/2011/04/blog-post.html"&gt;Как не надо писать код&lt;/a&gt; случай, может еще и цветочками показаться (поскольку мелкие изменения будут приводить к мессу не в одном классе, а еще и в десятке других). &lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8596733192274108952-7603098555910846375?l=sergeyteplyakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sergeyteplyakov.blogspot.com/feeds/7603098555910846375/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8596733192274108952&amp;postID=7603098555910846375' title='Комментарии: 4'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/7603098555910846375'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/7603098555910846375'/><link rel='alternate' type='text/html' href='http://sergeyteplyakov.blogspot.com/2011/08/c.html' title='Неявно типизированные поля в C#'/><author><name>Sergey Teplyakov</name><uri>https://profiles.google.com/108967431947412296254</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-2weHUILo1ns/AAAAAAAAAAI/AAAAAAAABRo/POab-anD6xA/s512-c/photo.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8596733192274108952.post-4161192631631301692</id><published>2011-08-23T17:46:00.001+03:00</published><updated>2011-08-24T15:30:42.955+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Книги'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>О книге “MS Visual C++ 2010 в среде .NET. Библиотека программиста”</title><content type='html'>&lt;p align="justify"&gt;Если посмотреть на мои обзоры книг (*), то может возникнуть подозрение в предвзятости, поскольку отзывы либо положительные, либо восторженные. Объясняется эта ситуация довольно просто: прежде чем браться за чтение книги разумно совершить «разведку боем» и выяснить, насколько чтение той или иной книги полезно и актуально. Если отзывы на книгу хорошие, автор внушает доверие, а тема интересна, то есть все шансы на то, что и вы не пожалеете о потерянном времени. Если же отзывы сугубо отрицательные и за версту тянет стилем изложения «Для чайников» в самом плохом смысле этого слова, то и время на такую книгу тратить не стоит.&lt;/p&gt;  &lt;p align="justify"&gt;Сегодня же будет исключение из этого правила, связанное, в первую очередь с тем, что это не совсем рецензия, а результаты упомянутой выше «разведки боем», однако ее было более чем достаточно, чтобы сложилось вполне конкретное впечатление об этом творении.&lt;/p&gt; &lt;a name='more'&gt;&lt;/a&gt;  &lt;p&gt;--------------------------------&lt;/p&gt;  &lt;p align="justify"&gt;&lt;a href="http://lh6.ggpht.com/-KpSt9nQT8zk/TlO9MrHgCcI/AAAAAAAABR4/H8i0DCfy9EQ/s1600-h/clip_image002%25255B4%25255D.jpg"&gt;&lt;img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; margin-left: 0px; border-left-width: 0px; margin-right: 0px" title="clip_image002" border="0" alt="clip_image002" align="left" src="http://lh4.ggpht.com/-GCn9WZvjf-o/TlO9NA30UDI/AAAAAAAABR8/060-pOtM77I/clip_image002_thumb%25255B1%25255D.jpg?imgmax=800" width="186" height="260" /&gt;&lt;/a&gt;Периодически я просматриваю новинки, которыми нас балуют наши отечественные издатели и вот на днях я увидел, что издательство Питер выпустило книгу под названием &lt;a href="http://www.ozon.ru/context/detail/id/7018874/"&gt;«MS Visual C++ 2010 в среде .NET»&lt;/a&gt; из серии «Библиотека программиста» (**) о Visual C++ от некоего Зиборова В.В. Поскольку сам факт выхода книги в такой серии много чего значит, то я не поленился прочитать аннотацию, пролистать содержание и фрагмент одной из глав, и вот, что я об этом думаю. &lt;/p&gt;  &lt;h4&gt;&lt;b&gt;1. Название и аннотация&lt;/b&gt;&lt;/h4&gt;  &lt;p align="justify"&gt;Название книги, как и ее обложка является важной составляющей любой книги, поскольку именно с них начинается знакомство с ней потенциального читателя. При этом желательно, чтобы название давало хотя бы минимальное представление о том, что читателя ожидает внутри, а взгляд на обложку не вывихивал глаз. Название этой книги намекает на то, что речь в ней пойдет о C++/CLI (это подтверждает и аннотация), но вот что о чем оно не говорит, так это о том, что книга является сборником рецептов. &lt;/p&gt;  &lt;p align="justify"&gt;Легкое гугление по фамилии автора дает понять, что это далеко не первое творение товарища Зиборова; помимо этой книги у него есть еще две: «&lt;a href="http://www.ozon.ru/context/detail/id/5134270/"&gt;Visual Basic 2010 на примерах&lt;/a&gt;» и «&lt;a href="http://www.ozon.ru/context/detail/id/5813708/"&gt;Visual C# на примерах&lt;/a&gt;» и даже беглое чтение аннотаций дает понять, что это три брата-акробата упакованные в разные обличия. Однако, в отличие от двух предыдущих книг, изданных издательством «БХВ-Петербург», товарищи из издательства «Питер» решили не отпугивать потенциальных читателей всякими «примерами», а назвать книгу более обтекаемо, что вполне логично.&lt;/p&gt;  &lt;p align="justify"&gt;&lt;b&gt;&lt;i&gt;Цитата:&lt;/i&gt;&lt;/b&gt;&lt;i&gt; «Представлено много различных авторских оригинальных решений задач программирования, которых читатель не сможет найти в интернете.» &lt;/i&gt;&lt;/p&gt;  &lt;p align="justify"&gt;Создатель аннотации откровенно жжот. Я все понимаю, у каждого из нас есть склонность к преувеличению, но писать о том, что в сборнике советов есть «оригинальные авторские решения», это явный перебор. Да и вообще, как в наш век интернета можно говорить об уникальных решениях, которые нельзя найти в интернете? Ведь если они такие уникальные, то о них будут трубить на всех известных форумах по разработке ПО и они через неделю они перестанут быть уникальными. Хотя, нужно признать, «уникальные» советы в книге правда присутствуют, но об этом позднее.&lt;/p&gt;  &lt;h4&gt;&lt;b&gt;2. Содержание&lt;/b&gt;&lt;/h4&gt;  &lt;p align="justify"&gt;Следующим, после названия книги и короткой аннотации, идет оглавление. Зачастую именно оглавление, а не аннотация дает понять читателю, что его ждет внутри. Читатель может получить представление о том, что же он получит в результате какие будут рассмотрены темы, какой объем посвящен тем или иным разделам, чтобы бы прикиуть более точно, о чем идет речь и какова глубина рассматриваемого материала.&lt;/p&gt;  &lt;p align="justify"&gt;Даже если книга построена на основе примеров, желательно, чтобы они были сгруппированы таким образом, чтобы максимально упростить восприятие нового материала. Так, например, разумно в один момент времени давать минимум новых концепций, рассматривая темы от простых к более сложным, рассматривая в одной главе максимально связные понятия. Это позволит читателю «погрузиться» в рассматриваемую тему и «абстрагироваться» от ненужных деталей, сосредоточившись на самом главном.&lt;/p&gt;  &lt;p align="justify"&gt;Теперь давайте посмотрим на содержание:&lt;/p&gt;  &lt;p align="justify"&gt;&lt;b&gt;Глава 1. Простейшие программы с экранной формой и элементами управления.      &lt;br /&gt;&lt;/b&gt;&lt;b&gt;…      &lt;br /&gt;&lt;/b&gt;&lt;b&gt;Пример 6. Управление стилем шрифта с помощью элемента управления &lt;/b&gt;&lt;b&gt;CheckBox      &lt;br /&gt;&lt;/b&gt;&lt;b&gt;Пример 7. Побитовый оператор «исключающее ИЛИ»      &lt;br /&gt;&lt;/b&gt;&lt;b&gt;Пример 8. Вкладки &lt;/b&gt;&lt;b&gt;TabControl и переключатели &lt;/b&gt;&lt;b&gt;RadioButton&lt;/b&gt;&lt;/p&gt;  &lt;p align="justify"&gt;Во-первых, накой начинать знакомство с новым языком программирования (а ведь если судить по аннотации, то это одна из целевых аудиторий книги) с пользовательского интерфейса, а не с простого консольного приложения? Я знаю, что есть категория читателей, которые с минимальными усилиями хотят увидеть «готовое приложение», но какое это имеет отношение к языку C++/CLI? Ведь в этом случае, читатель знакомится не только с новым языком программирования, но еще и с библиотекой Win Forms, что значительно усложняет восприятие материала.&lt;/p&gt;  &lt;p align="justify"&gt;Потом, мне вообще не понятна группировка тем. Ну кто потом догадается найти пример с «исключающим ИЛИ» в главе о «простейших программах с экранной формой», да еще и между примерами с CheckBox-ом и TabControl-ом? Да и вообще, раз взялись за битовые операторы, так разумно довести дело до конца и рассмотреть их все.&lt;/p&gt;  &lt;p align="justify"&gt;&lt;b&gt;Глава 2. Программирование консольных приложений ... 47      &lt;br /&gt;&lt;/b&gt;&lt;b&gt;Пример 12. Ввод и вывод в консольном приложении ... 47      &lt;br /&gt;&lt;/b&gt;&lt;b&gt;...      &lt;br /&gt;&lt;/b&gt;&lt;b&gt;Пример 14. Вызов метода &lt;/b&gt;&lt;b&gt;MessageBox::&lt;/b&gt;&lt;b&gt;Show в консольном приложении. Формат даты и времени ... 51      &lt;br /&gt;&lt;/b&gt;&lt;b&gt;Пример 15. Вызов функций &lt;/b&gt;&lt;b&gt;Visual &lt;/b&gt;&lt;b&gt;Basic из программы С++ ... 52      &lt;br /&gt;&lt;/b&gt;&lt;b&gt;Пример 16. Замечательной структурой данных является словарь &lt;/b&gt;&lt;b&gt;Dictionary ... 53&lt;/b&gt;&lt;/p&gt;  &lt;p align="justify"&gt;Мне сложно себе представить, какое количество совершенно разнородной информации наваливается на бедного читателя на всего-лишь десятке страниц книги (я специально привел номера страниц). В чем ценность рассмотрения формата даты и времени в примере с MessageBox-ом, да еще и в главе о консольных приложениях? А в чем польза вызова функций Visual Basic из программы С++? Чего-то мне сдается, что это не самая распространенная задача и она нисколько не поможет продвинуться в изучении языка С++. А название примера 16 вообще похоже на неудачно переведенную электронным переводчиком фразу из книги «C# за 15 минут».&lt;/p&gt;  &lt;p align="justify"&gt;Знаете, почему книга Джона Скита &lt;a href="http://sergeyteplyakov.blogspot.com/2011/04/c-in-depth-2nd-edition.html"&gt;“C# In Depth”&lt;/a&gt; читается так легко? По одной простой причине: Джон рассматривается одну концепцию за раз (его знаменитая фраза “one step at a time”, которую он использует десяток раз в своей книге). Это позволяет читателю «абстрагироваться» от ненужных в данный момент времени деталей и сосредоточиться на главном: на изучении определенной концепции языка. В программировании мы ведь постоянно поступаем таким же образом для борьбы с неотъемлемой сложностью ПО; ведь мы гарантированно получим «месс», если свалим в одну сущность десяток не связанных друг с другом концепций.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;Глава 12. Другие задачи, решаемые с помощью &lt;/b&gt;&lt;b&gt;Windows &lt;/b&gt;&lt;b&gt;Application      &lt;br /&gt;&lt;/b&gt;&lt;b&gt;Пример 98. Управление прозрачностью форме      &lt;br /&gt;&lt;/b&gt;&lt;b&gt;Пример 99. Время по Гринвичу в полупрозрачной форме      &lt;br /&gt;&lt;/b&gt;&lt;b&gt;Пример 102. Воспроизведение звуков операционной системы&lt;/b&gt;&lt;/p&gt;  &lt;p align="justify"&gt;Если в главах, посвященных определенной тематике была чехарда, то что говорить о главе, которая специально посвящена всему и сразу. Когда воспроизведение звуков (кстати, тоже весьма сомнительная тема) рассматривается рядом со временем по Гринвичу, да еще и в полупрозрачной форме то это вызывает, как минимум улыбку. Неужто время по Гринвичу в непрозрачной форме или без формы вообще получить нельзя?&lt;/p&gt;  &lt;p align="justify"&gt;Между главами 2 и 12 автор рассматривает практически все темы, на которые у Троелсена ушло порядка тысячи страниц: начиная от работы с файлами, заканчивая технологией ADO.NET и использованием функций AutoCAD и созданием PDF-файла. Видимо взаимодействие с AutoCAD является действительно киллер-фичей, поскольку об этом говорится в аннотации, но мне почему-то кажется, что это не является такой уж частой задачей, ну да ладно, это дело вкуса. Идем дальше. Точнее, возвращаемся на одну главу назад, которой хочется посвятить отдельный раздел.&lt;/p&gt;  &lt;p&gt;&lt;b&gt;3. Глава 11. Использование технологии &lt;/b&gt;&lt;b&gt;LINQ&lt;/b&gt;.&lt;/p&gt;  &lt;p align="justify"&gt;Этой главе стоит уделить отдельный раздел по двум причинам. Во-первых, в C++/CLI нет никакого синтаксического сахара для создания анонимных делегатов (лямбды C++ 0x не в счет, это совсем другое дело), что делает использование LINQ-а весьма ... скажем так, «многословным» делом. А во-вторых, именно эта глава лежит в открытом доступе на сайте издательства, так что мы можем познакомиться не только с содержанием, но и с содержимым.&lt;/p&gt;  &lt;p align="justify"&gt;Поскольку эта книга все же является сборником примеров, то давайте сразу же рассмотрим начало одного из них:&lt;/p&gt;  &lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;bool&lt;/span&gt; Предикат(String ^ S)&lt;br /&gt;{&lt;br /&gt;&amp;#160; &lt;span style="color: green"&gt;// Если число букв равно шести, то возвращаем true:&lt;/span&gt;&lt;br /&gt;&amp;#160; &lt;span style="color: blue"&gt;bool&lt;/span&gt; A = &lt;span style="color: blue"&gt;false&lt;/span&gt;;&lt;br /&gt;&amp;#160; &lt;span style="color: blue"&gt;if&lt;/span&gt; (S-&amp;gt;Length == 6) A = &lt;span style="color: blue"&gt;true&lt;/span&gt;;&lt;br /&gt;&amp;#160; &lt;span style="color: blue"&gt;return&lt;/span&gt; A;&lt;br /&gt;}&lt;br /&gt;String ^ Предикат2(String ^ S)&lt;br /&gt;{&lt;br /&gt;&amp;#160; &lt;span style="color: green"&gt;// Переводим строку в верхний регистр:&lt;/span&gt;&lt;br /&gt;&amp;#160; S = S-&amp;gt;ToUpper();&lt;br /&gt;&amp;#160; &lt;span style="color: blue"&gt;return&lt;/span&gt; S;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;

&lt;p align="justify"&gt;Итак, в аннотации сказано, что эта книга предназначена «для начинающих программистов, программистов среднего уровня, а также для программистов, имеющих опыт разработки на других языках и желающих ускоренными темпами освоить новый для себя язык MS Visual C++/CLI». Но здесь же в десятке строк кода целый букет «запахов»: &lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;div align="justify"&gt;&lt;b&gt;использование русскоязычных имен&lt;/b&gt;; &lt;/div&gt;
  &lt;/li&gt;

  &lt;li&gt;
    &lt;div align="justify"&gt;&lt;b&gt;использование неинформативных имен функций и переменных&lt;/b&gt;; никогда, никогда, никогда, нет, вы не поняли, вообще &lt;b&gt;никогда не называйте подобным образом имена функций и переменных&lt;/b&gt;; это бред сивой кобылы, не имеющий права на существование даже в примерах! Самое смешное, что самый первый раздел этой книги называется так: &lt;i&gt;Что такое «хороший стиль программирования»?&lt;/i&gt;, видимо автор взял последние три слова в кавычки преднамеренно. Что за имена Предикат и Предикат2, а чем читателю поможет имя переменной A? &lt;/div&gt;
  &lt;/li&gt;

  &lt;li&gt;
    &lt;div align="justify"&gt;&lt;b&gt;изменение значение параметров&lt;/b&gt;; напомню, что изменение значения параметров считается моветоном, поскольку читатель кода должен четко понимать, что исходный объект &lt;b&gt;S&lt;/b&gt; в методе &lt;b&gt;Предикат2&lt;/b&gt; не изменился, поскольку сама ссылка передается по значению.&lt;/div&gt;
  &lt;/li&gt;

  &lt;li&gt;неверный стиль наименования; параметры и локальные переменные в среде .Net принято называть в виде camelCase-а, имена типа &lt;b&gt;S&lt;/b&gt; и &lt;b&gt;A &lt;/b&gt;этому правилу противоречат. &lt;/li&gt;

  &lt;li&gt;&lt;b&gt;Предикат2 – это не предикат&lt;/b&gt;; мне, например, всегда казалось, что предикат – это такая хрень, возвращающая &lt;b&gt;true&lt;/b&gt; или &lt;b&gt;false&lt;/b&gt;, и быстрое гугление &lt;a href="http://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B5%D0%B4%D0%B8%D0%BA%D0%B0%D1%82"&gt;это мнение подтверждает&lt;/a&gt;. &lt;/li&gt;
&lt;/ol&gt;

&lt;p align="justify"&gt;Я вообще не говорю о том, что можно сократить число строк в методе с 3-х до одной:&lt;/p&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;bool&lt;/span&gt; LengthEqualToSix(String^ s)&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;return&lt;/span&gt; s-&amp;gt;Length == 6;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;

&lt;p align="justify"&gt;Дальше идет использование всех этих «предикатов» с помощью статических методов класса Enumerable. Но поскольку в языке C++/CLI нет таких штук как методы расширения и Method Group Conversion, то вглядит это не самым лучшим образом:&lt;/p&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: green"&gt;// Из массива имен получаем список имен, длина которых - шесть букв:&lt;/span&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;auto&lt;/span&gt; Запрос = Enumerable::Where&amp;lt;String^&amp;gt;(Имена, &lt;span style="color: blue"&gt;gcnew&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Func&amp;lt;String ^,&lt;span style="color: blue"&gt;bool&lt;/span&gt;&amp;gt;(Предикат));&lt;br /&gt;&lt;span style="color: green"&gt;// Сортируем полученный список в алфавитном порядке:&lt;/span&gt;&lt;br /&gt;Запрос = Enumerable::OrderBy(Запрос, &lt;span style="color: blue"&gt;gcnew&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; Func&amp;lt;String ^,String ^&amp;gt;(Предикат2));&lt;br /&gt;&lt;/pre&gt;

&lt;p align="justify"&gt;Есть и другие перлы, типа определения поля с именем: &lt;b&gt;bool КуритЛи;&lt;/b&gt; в классе &lt;b&gt;Сотрудник&lt;/b&gt;, или создание списка сотрудников по массиву сотрудников перед использованием методов класса &lt;b&gt;Enumerable &lt;/b&gt;(видимо автор не знает, что к массиву статические методы класса &lt;b&gt;Enumerable&lt;/b&gt; применимы точно так же, как и листу); и предикаты следующего вида:&lt;/p&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;bool&lt;/span&gt; Предикат(Сотрудник S)&lt;br /&gt;{&lt;br /&gt;&amp;#160; &lt;span style="color: green"&gt;// Если сотрудник не курит, то заносим его в список некурящих:&lt;/span&gt;&lt;br /&gt;&amp;#160; &lt;span style="color: blue"&gt;bool&lt;/span&gt; A = &lt;span style="color: blue"&gt;false&lt;/span&gt;;&lt;br /&gt;&amp;#160; &lt;span style="color: blue"&gt;if&lt;/span&gt; (S.КуритЛи == &lt;span style="color: blue"&gt;false&lt;/span&gt;) A = &lt;span style="color: blue"&gt;true&lt;/span&gt;;&lt;br /&gt;&amp;#160; &lt;span style="color: blue"&gt;return&lt;/span&gt; A;&lt;br /&gt;&amp;#160; &lt;span style="color: green"&gt;// Можно было бы записать короче:&lt;/span&gt;&lt;br /&gt;&amp;#160; &lt;span style="color: green"&gt;// return !S.КуритЛи;&lt;/span&gt;&lt;br /&gt;&amp;#160; &lt;span style="color: green"&gt;// но более запутанно&lt;/span&gt;&lt;br /&gt;}&lt;br /&gt; &lt;br /&gt;Месяц Предикат2(Месяц t)&lt;br /&gt;{&lt;br /&gt;&amp;#160; &lt;span style="color: blue"&gt;return&lt;/span&gt; t;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;

&lt;p align="justify"&gt;Чем автору не понравилась альтернативная запись и почему переменная типа &lt;b&gt;Месяц&lt;/b&gt; называется &lt;b&gt;t&lt;/b&gt; и почему только в этом случае имя переменной записано в нижнем регистре, я не знаю.&lt;/p&gt;

&lt;h4&gt;&lt;b&gt;Заключение&lt;/b&gt;&lt;/h4&gt;

&lt;p align="justify"&gt;Я хочу, чтобы меня поняли правильно: у меня нет ни малейших претензий к автору, который написал одну книгу и растиражировал ее, используя примеры на разных языках программирования. И у меня не было бы никаких вопросов к издательству, если бы обложка этой книги была бы пожелтее и вышла бы она в серии «Для чайников». Но выпускать ТАКОЕ в популярной и известной серии, рядом со знаменитыми работами таких признанных мировых экспертов, как Спольски, Бокс, Хант, Макконнелл и многих других, кажется просто преступлением.&lt;/p&gt;

&lt;p align="justify"&gt;Ребята издатели, существует масса интересных книг по программированию на платформе .Net, в том числе и на языке C++/CLI, которые могут занять достойное место в библиотеке программиста, но эта книга явно не из таких (***).&lt;/p&gt;

&lt;p align="justify"&gt;Что касается потенциального читателя, то для этой книги я его просто не вижу: примеры в книге слишком низкого качества, чтобы их можно было использовать даже начинающему, а стиль повествования и группировка материала таковы, что они едва ли задержатся в голове неподготовленного читателя. Читатель же подготовленный будет относится к этой книге скорее как к юмористической книге, нежели как к книге по программированию.&lt;/p&gt;

&lt;p align="justify"&gt;Да, чуть не забыл, чтобы долго не рылись, &lt;a href="http://piter.com/upload/contents/978545900786/978545900786_X.pdf"&gt;вот содержание&lt;/a&gt;, а &lt;a href="http://piter.com/upload/contents/978545900786/978545900786_p.pdf"&gt;вот фрагмент главы&lt;/a&gt;.&lt;/p&gt;

&lt;p align="justify"&gt;---------------&lt;/p&gt;

&lt;p align="justify"&gt;(*) Все мои рецензии, а также некоторые обзоры и анонсы книг можно найти &lt;a href="http://sergeyteplyakov.blogspot.com/search/label/%D0%9A%D0%BD%D0%B8%D0%B3%D0%B8"&gt;здесь&lt;/a&gt;.&lt;/p&gt;

&lt;p align="justify"&gt;(**) Нужно сказать, что в этой серии выходят неплохие книги, такие, например, как «Философия Java» Эккеля, «Чистый код» Мартина, «Паттерны проектирования» банды четырех и многие другие. Я имею ввиду, что это не серия типа «Изучи все на свете за 15 минут во сне», это вполне серьезная серия книг. &lt;/p&gt;

&lt;p align="justify"&gt;(***) В качестве отправной точки при поиске книг по программированию на платформе .Net можно отталкиваться от следующего списка: &lt;a href="http://sergeyteplyakov.blogspot.com/2011/04/cnet.html"&gt;Классические книги по C#/.NET&lt;/a&gt;.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8596733192274108952-4161192631631301692?l=sergeyteplyakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sergeyteplyakov.blogspot.com/feeds/4161192631631301692/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8596733192274108952&amp;postID=4161192631631301692' title='Комментарии: 8'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/4161192631631301692'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/4161192631631301692'/><link rel='alternate' type='text/html' href='http://sergeyteplyakov.blogspot.com/2011/08/ms-visual-c-2010-net.html' title='О книге “MS Visual C++ 2010 в среде .NET. Библиотека программиста”'/><author><name>Sergey Teplyakov</name><uri>https://profiles.google.com/108967431947412296254</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-2weHUILo1ns/AAAAAAAAAAI/AAAAAAAABRo/POab-anD6xA/s512-c/photo.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://lh4.ggpht.com/-GCn9WZvjf-o/TlO9NA30UDI/AAAAAAAABR8/060-pOtM77I/s72-c/clip_image002_thumb%25255B1%25255D.jpg?imgmax=800' height='72' width='72'/><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8596733192274108952.post-6873994350861193919</id><published>2011-08-17T00:13:00.001+03:00</published><updated>2011-08-17T00:22:21.020+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><category scheme='http://www.blogger.com/atom/ns#' term='Multithreading'/><title type='text'>Effective Concurrency от Герба Саттера</title><content type='html'>&lt;p align="justify"&gt;Герб Саттер, если вдруг кто не знает, это гуру С++ и многопоточности, автор нескольких весьма популярных книг и сотни известных статей, главный дизайнер языка C++/CLI и всяких там многопоточных расширений, в общем, крут парень, ничего не скажешь.&lt;/p&gt;  &lt;p align="justify"&gt;Но именно в данном случае интересно не это. Сегодня я отправил Гербу мыло с просьбой разрешить перевод и публикацию у себя в блоге его серии статей Effective Concurrency, и получил от него ответ через 40 (!) минут. То что ответ был отрицательным, это уже второе, но сам факт, что такие люди отвечают столь оперативно не может не радовать.&lt;/p&gt;  &lt;p align="justify"&gt;К сожалению, права на статьи серии Effective Concurrency принадлежат издательству Addison-Wesley, поскольку планируется выпуск одноименной книги, и эти ребята не разрешают никаких других публикаций, как минимум до выхода англоязычной книги в свет. А состоится этот выход в свет, по словам Герба, не раньше следующего года, ибо дел сейчас у него по горло (что не удивительно в свете событий, связанных с финальной стадией принятия нового стандарта С++).&lt;/p&gt;  &lt;p align="justify"&gt;Так что у нас, как всегда, две новости: одна хорошая, другая – не очень. Хорошая новость заключается в том, что Герб отличный парень и у нас скоро будет его новая книга, ну а плохая – что это “скоро” наступит не раньше следующего года.&lt;/p&gt;  &lt;h4&gt;Дополнительные ссылки&lt;/h4&gt;  &lt;ul&gt;   &lt;li&gt;     &lt;div align="justify"&gt;Ко всем статьям из серии Effective Concurrency можно достучаться через один из постов блога Герба по этой теме, например, отсюда: Effective Concurrency: &lt;a href="http://herbsutter.com/2010/09/24/effective-concurrency-know-when-to-use-an-active-object-instead-of-a-mutex/"&gt;Know When to Use an Active Object Instead of a Mutex&lt;/a&gt;&lt;/div&gt;   &lt;/li&gt;    &lt;li&gt;     &lt;div align="justify"&gt;Еще рекомендую посмотреть записи с Гербом на Channel9: &lt;/div&gt;   &lt;/li&gt;    &lt;ul&gt;     &lt;li&gt;       &lt;div align="justify"&gt;&lt;a href="http://channel9.msdn.com/Shows/Going+Deep/Conversation-with-Herb-Sutter-Perspectives-on-Modern-C0x11"&gt;Conversation with Herb Sutter: Perspectives on Modern C++(0x/11)&lt;/a&gt; – веселое интервью, в основном посвященное вопросам процесса стандартизации и новым фичам С++.&lt;/div&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;div align="justify"&gt;&lt;a href="http://channel9.msdn.com/Shows/Going+Deep/E2E-Herb-Sutter-and-Erik-Meijer-Perspectives-on-C"&gt;E2E: Herb Sutter and Erik Meijer - Perspectives on C++&lt;/a&gt; – серия с Эриком Мейером Expert To Expert, мне вообще кажется одной из самых интересных; так что, рекомендую&lt;/div&gt;     &lt;/li&gt;      &lt;li&gt;       &lt;div align="justify"&gt;&lt;a href="http://channel9.msdn.com/Tags/herb+sutter"&gt;Остальные записи с Гербом на Channel9&lt;/a&gt;&lt;/div&gt;     &lt;/li&gt;   &lt;/ul&gt; &lt;/ul&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8596733192274108952-6873994350861193919?l=sergeyteplyakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sergeyteplyakov.blogspot.com/feeds/6873994350861193919/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8596733192274108952&amp;postID=6873994350861193919' title='Комментарии: 7'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/6873994350861193919'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/6873994350861193919'/><link rel='alternate' type='text/html' href='http://sergeyteplyakov.blogspot.com/2011/08/effective-concurrency.html' title='Effective Concurrency от Герба Саттера'/><author><name>Sergey Teplyakov</name><uri>https://profiles.google.com/108967431947412296254</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-2weHUILo1ns/AAAAAAAAAAI/AAAAAAAABRo/POab-anD6xA/s512-c/photo.jpg'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8596733192274108952.post-2094189156585320562</id><published>2011-08-15T22:07:00.001+03:00</published><updated>2011-08-15T23:29:45.375+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.NET'/><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='C++'/><title type='text'>Гарантии безопасности исключений</title><content type='html'>&lt;p align="right"&gt;&lt;i&gt;Ошибки в обработке ошибок являются наиболее распространенным источником ошибок      &lt;br /&gt;&lt;/i&gt;Бредня, пришедшая в голову при написании этой статьи&lt;/p&gt;  &lt;p align="justify"&gt;Основные баталии по поводу того, что лучше использовать при программировании на C# – исключения или коды возврата для обработки, ушли в далекое прошлое (*), но до сих пор не утихают баталии другого рода: да, хорошо, мы остановились на обработке исключений, но как же нам их обрабатывать «правильно»?&lt;/p&gt;  &lt;p align="justify"&gt;Существует множество точек зрения о том, что же такое «правильно», большая часть из которых сводится к тому, что нужно перехватывать только те исключения, которые ты можешь обработать, а все остальные пробрасывать вызывающему коду. Ну, а в случае, если на верхний уровень пробралось дерзким образом непонятное исключение, то стрелять все приложение целиком, поскольку уже не понятно, находится ли оно, родимое, в согласованном состоянии или нет.&lt;/p&gt;  &lt;p align="justify"&gt;Существует множество «за» и «против» такого способа перехвата и обработки исключений, но сегодня я хочу рассмотреть несколько другую тему. А именно тему обеспечения согласованного состояния приложения в свете возникновения исключения – три уровня безопасности исключений.&lt;/p&gt; &lt;a name='more'&gt;&lt;/a&gt;  &lt;h4&gt;Три типа гарантий&lt;/h4&gt;  &lt;p align="justify"&gt;В конце 90-х годов Дейв Абрахамс (Dave Abrahams) предложил три уровня безопасности исключений: базовая гарантия, строгая гарантия и гарантия отсутствия исключений. Эта идея была тепло встречена сообществом С++ разработчиков, а после ее популяризации (и некоторой модификации) Гербом Саттером, гарантии безопасности исключений стали широко применяться в boost-е, в стандартной библиотеке С++, а также при разработке прикладных приложений.&lt;/p&gt;  &lt;p align="justify"&gt;Изначально эти гарантии были предложены Дейвом Абрахамсом для реализации библиотеки STLPort на языке С++, но сама идея безопасности исключений не привязана к конкретному языку программирования и может использоваться в других языках, использующих исключения в качестве основного механизма обработки ошибок, таких как Java или C#. Кроме того, в настоящее время существует две версии определений гарантии безопасности исключений: (1) исходная версия, предложенная Дейвом Абрахамсом и (2) модифицированная версия, популяризированная Саттером и Страуструпом, и более подходящая не только для библиотек, но и для прикладных приложений. &lt;/p&gt;  &lt;h5&gt;Базовая гарантия&lt;/h5&gt;  &lt;p align="justify"&gt;&lt;b&gt;Исходное определение&lt;/b&gt;: “&lt;i&gt;в случае возникновения исключений не должно быть никаких утечек ресурсов&lt;/i&gt;”.&lt;/p&gt;  &lt;p align="justify"&gt;&lt;b&gt;Современное определение&lt;/b&gt;: “&lt;i&gt;при возникновении любого исключения в некотором методе, состояние программы должно оставаться согласованным&lt;/i&gt;”. Это означает, не только отсутствие утечек ресурсов, но и сохранение инвариантов класса, что является более общим критерием, по сравнению с базовым определением.&lt;/p&gt;  &lt;p align="justify"&gt;Разница между этими двумя формулировками обусловлена тем, что изначально эта гарантия были предложена для реализации библиотеки на языке С++ и не имела никакого отношения к прикладным приложениями. Но если говорить о более общем случае (т.е. о приложении, а не только о библиотеке), то можно сказать, что утечки ресурсов являются лишь одним из источников багов, но далеко не единственным. Сохранение инварианта в любой устойчивый момент времени (**) является залогом того, что никакой внешний код не сможет «увидеть» рассогласованного состояния приложения, что, согласитесь, не менее важно, чем отсутствие утечек ресурсов. Мало какого пользователя банковского приложения будут интересовать утечки памяти, если при переводе денег с одного счета на другой, деньги могут «уйти» с одного счета, но «не дойти» до другого.&lt;/p&gt;  &lt;h5&gt;Строгая гарантия&lt;/h5&gt;  &lt;p align="justify"&gt;Что касается определения строгой гарантии исключений, то исходное и современные определения являются аналогичными и сводятся к следующему: “&lt;i&gt;если при выполнении операции возникает исключение, то это не должно оказать какого-либо влияния на состояние приложения&lt;/i&gt; ”.&lt;/p&gt;  &lt;p align="justify"&gt;Другими словами, строгая гарантия исключений обеспечивает транзакционность операций, когда мы получаем либо все, либо ничего. В этом случае, при возникновении исключения мы должны откатиться к состоянию приложения, которое было перед выполнением операции, и переходить в новое состояние только в случае удачного завершения всей операции.&lt;/p&gt;  &lt;h5&gt;Гарантия отсутствия исключений&lt;/h5&gt;  &lt;p align="justify"&gt;Гарантия отсутствия исключений сводится к следующему: “&lt;i&gt;ни при каких обстоятельствах функция не будет генерировать исключения&lt;/i&gt;”.&lt;/p&gt;  &lt;p align="justify"&gt;Эта гарантия наиболее простая с точки зрения определения, однако, она не так проста, как кажется. Во-первых, ее практически невозможно обеспечить в общем случае, особенно в среде .Net, когда исключение может произойти практически в любой точке приложения. На практике, лишь единицы операций следуют этой гарантии, и именно на основании таких операций строятся гарантии предыдущих уровней. В языке C#, то одной из немногих операций, обеспечивающих эту гарантию, является присваивание ссылок, а в языке C++ - функция swap, реализующая обмен значений. Именно на основании этих функций зачастую и реализуется строгая гарантия исключений, когда вся «грязная работа» выполняется во временном объекте, который затем присваивается результирующему значению.&lt;/p&gt;  &lt;p align="justify"&gt;Во-вторых, в некоторых случаях невозможно обеспечить нормальную работу других функций, если некоторые операции не будут следовать гарантии отсутствия исключений. Так, например, в языке С++, для обеспечения даже базовой гарантии исключений (а точнее утечки ресурсов) в контейнерах необходимо, чтобы деструктор пользовательского типа не генерировал исключения. &lt;/p&gt;  &lt;p align="justify"&gt;Рассмотренные выше три гарантии безопасности исключений идут от наиболее слабой к наиболее сильной; при этом каждая последующая гарантия является надмножеством предыдущей. Это означает, что выполнение строгой гарантии автоматом влечет за собой выполнение базовой гарантии, а гарантия отсутствия исключений влечет за собой выполнение строгой гарантии. Если же код не отвечает даже базовой гарантии исключений, то он является миной замедленного действия в вашем приложении и рано или поздно приведет к неприятным последствиям, развалив к чертям его состояние.&lt;/p&gt;  &lt;p align="justify"&gt;Теперь давайте рассмотрим несколько примеров.&lt;/p&gt;  &lt;h4&gt;Примеры нарушения базовой гарантии&lt;/h4&gt;  &lt;p align="justify"&gt;Главным способом предотвращения утечек памяти и ресурсов в языке C++ является идиома &lt;a href="http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization"&gt;RAII&lt;/a&gt; (Resource Acquisition Is Initialization), которая заключается в том, что объект захватывает ресурс в конструкторе и освобождает его в деструкторе. А поскольку вызов деструктора осуществляется автоматически при выходе объекта из области видимости по любой причине, в том числе и при возникновении исключения, то неудивительно, что эта же идиома используется и для обеспечения безопасности исключений.&lt;/p&gt;  &lt;p align="justify"&gt;В язык C# эта идиома перекочевала в виде интерфейса &lt;b&gt;IDisposable&lt;/b&gt; и конструкции &lt;b&gt;using&lt;/b&gt;, однако, в отличие от С++ она применима для управления временем жизни ресурса в некоторой области видимости, и не подходит для управления множеством ресурсов, захватываемых в конструкторе.&lt;/p&gt;  &lt;p align="justify"&gt;Давайте рассмотрим такой пример:&lt;/p&gt;  &lt;pre style="font-family: consolas"&gt;&lt;span style="color: green"&gt;// Некоторый класс, содержащий управляемые ресурсы&lt;/span&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;class&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;DisposableA&lt;/span&gt; : &lt;span style="color: #2b91af"&gt;IDisposable&lt;/span&gt;&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;void&lt;/span&gt; Dispose() {}&lt;br /&gt;}&lt;br /&gt; &lt;br /&gt;&lt;span style="color: green"&gt;// Еще один класс с управляемыми ресурсами&lt;/span&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;class&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;DisposableB&lt;/span&gt; : &lt;span style="color: #2b91af"&gt;IDisposable&lt;/span&gt;&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;public&lt;/span&gt; DisposableB()&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; disposableA = &lt;span style="color: blue"&gt;new&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;DisposableA&lt;/span&gt;();&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;throw&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;new&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;Exception&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;quot;OOPS!&amp;quot;&lt;/span&gt;);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;void&lt;/span&gt; Dispose() {}&lt;br /&gt; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;private&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;DisposableA&lt;/span&gt; disposableA;&lt;br /&gt;}&lt;br /&gt; &lt;br /&gt;&lt;span style="color: green"&gt;// Где-то в приложении&lt;/span&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;using&lt;/span&gt; (&lt;span style="color: blue"&gt;var&lt;/span&gt; disposable = &lt;span style="color: blue"&gt;new&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;DisposableB&lt;/span&gt;())&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// Упс! Метод Dispose не будет вызван ни для &lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// DisposableB, ни для DisposableA&lt;/span&gt;&lt;br /&gt;}&lt;/pre&gt;

&lt;p align="justify"&gt;Итак, у нас есть два disposable-класса: &lt;b&gt;DisposableA&lt;/b&gt; и &lt;b&gt;DisposableB&lt;/b&gt;, каждый из которых захватывает некоторый управляемый ресурс в конструкторе и освобождает его в методе &lt;b&gt;Dispose&lt;/b&gt;. Давайте пока не будем рассматривать финализатор, поскольку он никак не поможет нам гарантировать детерминированный порядок освобождения ресурсов, что в некотором случае является жизненно важным. &lt;/p&gt;

&lt;p align="justify"&gt;В данном случае, при генерации исключения конструктором класса &lt;b&gt;DisposableB&lt;/b&gt; мы никогда не вызовем метод &lt;b&gt;Dispose&lt;/b&gt;, поскольку объект &lt;b&gt;disposable&lt;/b&gt; никогда не существовал. В этом плане поведение у большинства mainstream языков программирования более или менее одинаковое, но есть и некоторые отличия. Сходство заключается в том, что если конструктор «упадет», то вызывающий код так и не сможет получить ссылку на еще не сконструированный объект и явно освободить его ресурсы. Однако, в отличие от языка С++, в котором вызов деструктора полностью сконструированных полей осуществляется автоматически, в «управляемом» языке C# этого не происходит (***).Если конструктор класса &lt;b&gt;DisposableB&lt;/b&gt; сгенерирует исключение и не освободит уже захваченные ранее ресурсы самостоятельно, мы получим «утечку ресурсов» (или, как минимум, недетерминированное их освобождение).&lt;/p&gt;

&lt;p align="justify"&gt;Эта же проблема может проявляться и более тонким образом. В рассмотренном ранее случае, явно видно, что мы создали экземпляр disposable-объекта, после чего генерируется исключение. Но бывают случаи, когда отсутствие базовой гарантии исключений увидеть немного сложнее.&lt;/p&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;class&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;Base&lt;/span&gt; : &lt;span style="color: #2b91af"&gt;IDisposable&lt;/span&gt;&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;public&lt;/span&gt; Base()&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// Захватываем некоторый ресурс&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;void&lt;/span&gt; Dispose() {}&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;br /&gt;}&lt;br /&gt; &lt;br /&gt;&lt;span style="color: blue"&gt;class&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;Derived&lt;/span&gt; : &lt;span style="color: #2b91af"&gt;Base&lt;/span&gt;, &lt;span style="color: #2b91af"&gt;IDisposable&lt;/span&gt;&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;public&lt;/span&gt; Derived(&lt;span style="color: blue"&gt;object&lt;/span&gt; data)&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;if&lt;/span&gt; (data == &lt;span style="color: blue"&gt;null&lt;/span&gt;)&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;throw&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;new&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;ArgumentNullException&lt;/span&gt;(&lt;span style="color: #a31515"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// OOPS!!&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt; &lt;br /&gt;&lt;span style="color: green"&gt;// И снова где-то в приложении&lt;/span&gt;&lt;br /&gt;&lt;span style="color: blue"&gt;using&lt;/span&gt; (&lt;span style="color: blue"&gt;var&lt;/span&gt; derived = &lt;span style="color: blue"&gt;new&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;Derived&lt;/span&gt;(&lt;span style="color: blue"&gt;null&lt;/span&gt;))&lt;br /&gt;{}&lt;/pre&gt;

&lt;p align="justify"&gt;Генерация исключения в конструкторе класса &lt;b&gt;Derived&lt;/b&gt; нарушает базовую гарантию исключений и приводит к утечке ресурсов, поскольку метод &lt;b&gt;Dispose&lt;/b&gt; класса &lt;b&gt;Base&lt;/b&gt; не вызывается (****). Опять таки, поскольку компилятор знает об интерфейсе &lt;b&gt;IDisposable&lt;/b&gt; только через призму конструкции &lt;b&gt;using&lt;/b&gt;, то во всех случаях, когда disposable объект является полем другого класса, за вызов метода &lt;b&gt;Dispose&lt;/b&gt; отвечает только программист.&lt;/p&gt;

&lt;p align="justify"&gt;Помимо базового класса, подобную же шутку может сыграть и инициализаторы полей, когда конструктор одного из полей может генерировать исключение:&lt;/p&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;class&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;ComposedDisposable&lt;/span&gt; : &lt;span style="color: #2b91af"&gt;IDisposable&lt;/span&gt;&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;void&lt;/span&gt; Dispose() {}&lt;br /&gt; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;private&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;readonly&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;DisposableA&lt;/span&gt; disposableA = &lt;span style="color: blue"&gt;new&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;DisposableA&lt;/span&gt;();&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// А что, если конструктор DisposableB упадет? OOPS!!&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;private&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;readonly&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;DisposableB&lt;/span&gt; disposableB = &lt;span style="color: blue"&gt;new&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;DisposableB&lt;/span&gt;();&lt;br /&gt;}&lt;/pre&gt;

&lt;p align="justify"&gt;В данном случае, если конструктор класса &lt;b&gt;DisposableB&lt;/b&gt; при инициализации поля &lt;b&gt;disposableB&lt;/b&gt; сгенерирует исключение, то перехватить его и освободить уже захваченные ресурсы будет невозможно. В С++ существует такая вещь, как перехват исключений, возникших в списке инициализации (см. &lt;a href="http://my.safaribooksonline.com/book/programming/cplusplus/0201700735/exception-handling/373"&gt;Exception and Member Initialization&lt;/a&gt;), однако такая возможность в языке C# отсутствует, поэтому выход из этой ситуации один: старайтесь ее не допускать.&lt;/p&gt;

&lt;p align="justify"&gt;Что касается всех предыдущих случаев, то обеспечение базовой гарантии исключений полностью ложится на плечи разработчика, поскольку никакого «сахара» для этих целей язык C# не предоставляет. &lt;b&gt;Все, что нам остается, это либо создавать подобъекты в нужном порядке и &lt;/b&gt;&lt;b&gt;disposable поле создавать в самом конце конструктора, либо оборачивать их создание в блок try/catch и очищать все ресурсы, в случае возникновения исключения.&lt;/b&gt;&lt;/p&gt;

&lt;h4&gt;Пример строгой гарантии исключений. Object initializer и collection initializer&lt;/h4&gt;

&lt;p align="justify"&gt;Примеры нарушения базовой гарантии исключений, приведенные выше, хоть и не являются надуманными, но встречаются не так и часто. И если в случае создания объектов, содержащих несколько управляемых ресурсов, компилятор языка C# ничем нам помочь не может, то он может помочь в некоторых других случаях, например, при создании объектов и коллекций.&lt;/p&gt;

&lt;p align="justify"&gt;Инициализатор объектов и коллекций (object initializer и collection initializer) обеспечивают атомарность создания и инициализации объекта или заполнения коллекции списком элементов. Давайте рассмотрим следующий пример.&lt;/p&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;class&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;Person&lt;/span&gt;&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;string&lt;/span&gt; FirstName { &lt;span style="color: blue"&gt;get&lt;/span&gt;; &lt;span style="color: blue"&gt;set&lt;/span&gt;; }&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;string&lt;/span&gt; LastName { &lt;span style="color: blue"&gt;get&lt;/span&gt;; &lt;span style="color: blue"&gt;set&lt;/span&gt;; }&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;int&lt;/span&gt; Age { &lt;span style="color: blue"&gt;get&lt;/span&gt;; &lt;span style="color: blue"&gt;set&lt;/span&gt;; }&lt;br /&gt;}&lt;br /&gt; &lt;br /&gt;&lt;span style="color: blue"&gt;var&lt;/span&gt; person = &lt;span style="color: blue"&gt;new&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;Person&lt;/span&gt;&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; FirstName = &lt;span style="color: #a31515"&gt;&amp;quot;Bill&amp;quot;&lt;/span&gt;,&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; LastName = &lt;span style="color: #a31515"&gt;&amp;quot;Gates&amp;quot;&lt;/span&gt;,&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; Age = 55,&lt;br /&gt;};&lt;/pre&gt;

&lt;p align="justify"&gt;С первого взгляда может показаться, что это всего лишь синтаксический сахар для следующего:&lt;/p&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;var&lt;/span&gt; person = &lt;span style="color: blue"&gt;new&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;Person&lt;/span&gt;();&lt;br /&gt;person.FirstName = &lt;span style="color: #a31515"&gt;&amp;quot;Bill&amp;quot;&lt;/span&gt;;&lt;br /&gt;person.LastName = &lt;span style="color: #a31515"&gt;&amp;quot;Gates&amp;quot;&lt;/span&gt;;&lt;br /&gt;person.Age = 55;&lt;/pre&gt;

&lt;p align="justify"&gt;Однако на самом деле, &lt;b&gt;при вызове инициализатора объекта создается временная переменная, затем изменяются свойства именно этой переменной, и только потом она присваивается новому объекту&lt;/b&gt;:&lt;/p&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;var&lt;/span&gt; tmpPerson = &lt;span style="color: blue"&gt;new&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;Person&lt;/span&gt;();&lt;br /&gt;tmpPerson.FirstName = &lt;span style="color: #a31515"&gt;&amp;quot;Bill&amp;quot;&lt;/span&gt;;&lt;br /&gt;tmpPerson.LastName = &lt;span style="color: #a31515"&gt;&amp;quot;Gates&amp;quot;&lt;/span&gt;;&lt;br /&gt;tmpPerson.Age = 55;&lt;br /&gt;&lt;span style="color: blue"&gt;var&lt;/span&gt; person = tmpPerson;&lt;/pre&gt;

&lt;p align="justify"&gt;Это обеспечивает атомарность процесса создания объекта и невозможность использования частично инициализированного объекта в случае генерации исключения одним из setter-ов. Аналогичный принцип лежит и в инициализаторе коллекций, когда объекты добавляются во временную коллекцию и лишь после ее заполнения временная переменная присваивается новому объекту.&lt;/p&gt;

&lt;p align="justify"&gt;Принцип, заложенный в основе этих двух концепций, может легко быть использован при собственной реализации строгой гарантии исключений в собственном коде. Для этого достаточно все изменения внутреннего состояния объекта выполнять в некоторой временной переменной и лишь после их завершения атомарно изменять его реальное состояние.&lt;/p&gt;

&lt;h4&gt;Заключение&lt;/h4&gt;

&lt;p align="justify"&gt;Корректная обработка исключений дело не простое, и, как показали некоторые примеры, иногда даже базовая гарантия исключений дается с трудом. Однако обеспечение подобных гарантий является жизненно необходимым условием при разработке приложений, поскольку значительно легче всю сложность по работе ресурсами упрятать в одном месте, нежели размазать ее тонким слоем по всему приложению. Золотое правило, сформулированное десяток лет назад Скоттом Мейерсом все еще остается в силе: &lt;i&gt;создавайте классы, которые легко использовать правильно и сложно использовать неправильно&lt;/i&gt;, и гарантия исключений в этом играет явно не последнюю роль.&lt;/p&gt;

&lt;p align="justify"&gt;Если говорить о практическом применении данных гарантий, то следует помнить несколько моментов. Во-первых, код, не выполняющий базовую гарантию исключений некорректен; на его основе просто невозможно создать приложение, чье состояние не будет разламываться при его использовании или изменении (*****). Во-вторых, не стоит параноить и добиваться максимальной гарантии. Добиться гарантии отсутствия исключений на 100% вообще практически невозможно из-за наличия асинхронных исключений, но даже реализация строгой гарантии во многих случаях может быть неоправданно дорогой.&lt;/p&gt;

&lt;p align="justify"&gt;В заключение можно сказать следующее: &lt;em&gt;гарантии безопасности исключений – не панацея, но отличный фундамент для построения надежных приложений&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;----------------------&lt;/p&gt;

&lt;p align="justify"&gt;(*) На самом деле, «горячих» дебатов, в общем-то, и не было по одной простой причине: вы не можете программировать на платформе .Net без обработки исключений. Подобные дебаты актуальны, например, в языке C++, особенно, когда речь заходит о низкоуровневом программировании.&lt;/p&gt;

&lt;p align="justify"&gt;(**) В общем случае, никто не требует сохранение инварианта всегда; обычно требуется сохранение инварианта «до» и «после» вызова &lt;b&gt;открытого&lt;/b&gt; метода, но совсем необязательно его сохранение после вызова &lt;b&gt;закрытого&lt;/b&gt; метода, выполняющего лишь «часть» работы.&lt;/p&gt;

&lt;p align="justify"&gt;(***) Может показаться весьма забавным тот факт, что более «навороченный» язык, такой как C# может не делать чего-то, что делает старина С++, но это действительно так. Давайте в качестве примера, перепишем рассмотренный ранее код с C# на C++:&lt;/p&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;class&lt;/span&gt; Resource1&lt;br /&gt;{&lt;br /&gt;&lt;span style="color: blue"&gt;public&lt;/span&gt;:&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; Resource1()&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// Захватываем некоторый ресурс, будь-то выделяем память&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// в куче или создаем дескриптор ОС&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; ~Resource1()&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// Освобождаем захваченный ресурс&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;};&lt;br /&gt; &lt;br /&gt;&lt;span style="color: blue"&gt;class&lt;/span&gt; Resource2&lt;br /&gt;{&lt;br /&gt;&lt;span style="color: blue"&gt;public&lt;/span&gt;:&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; Resource2()&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// В этой точке кода объект resource1_ уже проинициализирован&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;throw&lt;/span&gt; std::exception(&lt;span style="color: #a31515"&gt;&amp;quot;Yahoo!&amp;quot;&lt;/span&gt;);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&lt;span style="color: blue"&gt;private&lt;/span&gt;:&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; Resource1 resource1_;&lt;br /&gt;};&lt;br /&gt; &lt;br /&gt;&lt;br /&gt;&lt;span style="color: green"&gt;// где-то в приложении создаем экземпляр класса Resource2&lt;/span&gt;&lt;br /&gt;Resource2 resource2;&lt;/pre&gt;

&lt;p align="justify"&gt;Как уже было сказано ранее в языке С++ (в отличие от языка C#), при генерации исключения в конструкторе класса деструкторы уже сконструированных полей (т.е. подобъектов) будут вызваны автоматически. Это значит, что в данном случае вызов деструктора объекта &lt;b&gt;Resource1&lt;/b&gt; произойдет автоматически и никаких утечек ресурсов не будет. &lt;/p&gt;

&lt;p align="justify"&gt;Такие отличия в поведения языков C# и C++ легко объяснимо. В языке С++ ресурсом является все, включая динамически выделенную память, поэтому и средства управления ресурсами находятся на более высоком уровне. Прикладной же программист, работающий с языком C#, значительно чаще использует ресурсы в блоке using, нежели захватывает ресурсы в конструкторе. И если же он сталкивается с такой задачей, то решить ее придется ему самостоятельно, без помощи компилятора.&lt;/p&gt;

&lt;p align="justify"&gt;Кстати, Герб Саттер уже рассказывал об этом когда-то в своей заметке: &lt;a href="http://herbsutter.com/2008/07/25/constructor-exceptions-in-c-c-and-java/"&gt;“Constructor Exceptions in C++, C#, and Java”&lt;/a&gt;.&lt;/p&gt;

&lt;p align="justify"&gt;(****) Может я уже и достал с этими примечаниями, но это достаточно важное и, кажется, предпоследнее. Подобный пример достаточно часто любят задавать на собеседованиях, так что теперь, мои читатели знают на него правильный ответ!&lt;/p&gt;

&lt;p align="justify"&gt;(*****) Все сказанное в этой статье относится только к синхронным исключениям, поскольку гарантировать согласованное возникновении «асинхронных» исключений, таких как &lt;b&gt;OutOfMemoryException&lt;/b&gt; или &lt;b&gt;ThreadAbortException&lt;/b&gt; практически невозможно. За пруфом сюда: «&lt;a href="http://sergeyteplyakov.blogspot.com/2011/01/threadabort.html"&gt;О вреде метода Thread.Abort&lt;/a&gt;».&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/8596733192274108952-2094189156585320562?l=sergeyteplyakov.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://sergeyteplyakov.blogspot.com/feeds/2094189156585320562/comments/default' title='Комментарии к сообщению'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=8596733192274108952&amp;postID=2094189156585320562' title='Комментарии: 5'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/2094189156585320562'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/8596733192274108952/posts/default/2094189156585320562'/><link rel='alternate' type='text/html' href='http://sergeyteplyakov.blogspot.com/2011/08/blog-post_15.html' title='Гарантии безопасности исключений'/><author><name>Sergey Teplyakov</name><uri>https://profiles.google.com/108967431947412296254</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='32' src='//lh5.googleusercontent.com/-2weHUILo1ns/AAAAAAAAAAI/AAAAAAAABRo/POab-anD6xA/s512-c/photo.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-8596733192274108952.post-2534254581143475700</id><published>2011-08-01T22:19:00.001+03:00</published><updated>2011-08-01T22:19:47.345+03:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='C#'/><category scheme='http://www.blogger.com/atom/ns#' term='Multithreading'/><title type='text'>О синглтонах и статических конструкторах</title><content type='html'>&lt;p align="justify"&gt;Изначально автор хотел назвать эту статью следующим образом: «О синглтонах, статических конструкторах и инициализаторах статических полей, о флаге beforeFieldInit и о его влиянии на deadlock-и статических конструкторов при старте сервисов релизных билдов в .Net Framework 3.5», однако в связи с тем, что многострочные названия по неведомой автору причине так и не прижились в современном компьютерном сообществе, он (автор) решил сократить это название, чудовищным образом исказив его исходный смысл.&lt;/p&gt;  &lt;p align="justify"&gt;-------------------------&lt;/p&gt;  &lt;p align="justify"&gt;Любая реализация паттерна Синглтон в общем случае преследует две цели: во-первых, реализация должна быть потокобезопасной, чтобы предотвратить создание более одного экземпляра в многопоточном мире .Net; а во-вторых, эта реализация должна быть «отложенной» (lazy), чтобы не создавать экземпляр (потенциально) дорого объекта раньше времени или в тех случаях, когда он вообще может не понадобиться. Но поскольку основное внимание при прочтении любой статьи про реализацию Синглтона отводится многопоточности, то на «ленивость» зачастую не хватает ни времени не желания.&lt;/p&gt;  &lt;div align="justify"&gt;&lt;a name='more'&gt;&lt;/a&gt;&lt;/div&gt;  &lt;p align="justify"&gt;Давайте рассмотрим одну из наиболее простых и популярных реализаций паттерна Синглтон (*), основанную на инициализаторе статического поля:&lt;/p&gt;  &lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;sealed&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;class&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;Singleton&lt;/span&gt;&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;private&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;static&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;readonly&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;Singleton&lt;/span&gt; instance = &lt;span style="color: blue"&gt;new&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;Singleton&lt;/span&gt;();&lt;br /&gt; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// Explicit static constructor to tell C# compiler&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;// not to mark type as beforefieldinit&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;static&lt;/span&gt; Singleton()&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; { }&lt;br /&gt; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;private&lt;/span&gt; Singleton()&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; { }&lt;br /&gt; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;static&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;Singleton&lt;/span&gt; Instance { &lt;span style="color: blue"&gt;get&lt;/span&gt; { &lt;span style="color: blue"&gt;return&lt;/span&gt; instance; } }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;

&lt;p align="justify"&gt;Потокобезопасность этой реализации основывается на том, что статический конструктор (или, другими словами, инициализатор типа) в одном домене гарантировано вызывается не более одного раза. А раз так, то со стороны разработчика не нужно делать никакие финты ушами ради того, чтобы сделать потокобезопасность еще более безопасной. Большинство разработчиков (и до недавнего времени и я в том числе) на этом успокаиваются, поскольку основную проблему, которая может произойти с любым синглтоном, мы уже решили, так что на комментарий пустого статического конструктора мало кто обращает внимание. А поскольку этот комментарий вообще &lt;s&gt;нифига&lt;/s&gt; не понятен, то пустой статический конструктор просто не доходит до многих реализаций в реальных приложениях. &lt;/p&gt;

&lt;h4&gt;&lt;b&gt;Статический конструктор и инициализаторы полей&lt;/b&gt;&lt;/h4&gt;

&lt;p align="justify"&gt;Статический конструктор – это штука, предназначенная для инициализации типа, которая должна быть вызвана перед доступом к любому статическому или не статическому члену, а также перед созданием экземпляра класса. Однако если класс в языке C# не содержит явного объявления статического конструктора, то компилятор помечает его атрибутом &lt;b&gt;beforeFieldInit&lt;/b&gt;, что говорит среде времени выполнения о том, что тип можно инициализировать отложенным (“relaxed”) образом. Однако, как показывает практика, в .Net Framework до 4-й версии, это поведение можно назвать каким угодно, но не «отложенным».&lt;/p&gt;

&lt;p align="justify"&gt;Итак, давайте рассмотрим следующий код:&lt;/p&gt;

&lt;pre style="font-family: consolas"&gt;&lt;span style="color: blue"&gt;class&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;Singleton&lt;/span&gt;&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;//static Singleton()&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;//{&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;//&amp;#160;&amp;#160;&amp;#160; Console.WriteLine(&amp;quot;.cctor&amp;quot;);&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: green"&gt;//}&lt;/span&gt;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;static&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;string&lt;/span&gt; S = Echo(&lt;span style="color: #a31515"&gt;&amp;quot;Field initializer&amp;quot;&lt;/span&gt;);&lt;br /&gt; &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;public&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;static&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;string&lt;/span&gt; Echo(&lt;span style="color: blue"&gt;string&lt;/span&gt; s)&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(s);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;return&lt;/span&gt; s;&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;}&lt;br /&gt; &lt;br /&gt;&lt;span style="color: blue"&gt;class&lt;/span&gt;&amp;#160;&lt;span style="color: #2b91af"&gt;Program&lt;/span&gt;&lt;br /&gt;{&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;static&lt;/span&gt;&amp;#160;&lt;span style="color: blue"&gt;void&lt;/span&gt; Main(&lt;span style="color: blue"&gt;string&lt;/span&gt;[] args)&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #a31515"&gt;&amp;quot;Starting Main...&amp;quot;&lt;/span&gt;);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: blue"&gt;if&lt;/span&gt; (args.Length == 1)&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; {&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: #2b91af"&gt;Singleton&lt;/span&gt;.S);&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: #2b91af"&gt;Console&lt;/span&gt;.ReadLine();&lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; }&lt;br /&gt;}&lt;/pre&gt;

&lt;p align="justify"&gt;Поскольку в данном случае явный статический конструктор класса &lt;b&gt;Singleton&lt;/b&gt; отсутствует, то компилятор к этому типу добавляет атрибут &lt;b&gt;beforeFieldInit&lt;/b&gt;. Согласно спецификации, в этом инициализация статического поля произойдет &lt;b&gt;до первого обращения&lt;/b&gt; к этому полю, причем может она может произойти задолго до этого обращения. На практике при использовании .Net Framework 3.5 и ниже, это приводит к тому, что инициализация статического поля произойдет до вызова метода Main, даже если условие &lt;b&gt;args.&lt;/b&gt;&lt;b&gt;Legnth == 1&lt;/b&gt; не будет выполнено. Все это приводит к тому, что при запуске указанного выше кода мы получим следующее: &lt;/p&gt;

&lt;p align="justify"&gt;&lt;font face="Consolas"&gt;Field initializer
    &lt;br /&gt;Starting Main...&lt;/font&gt;&lt;/p&gt;

&lt;p align="justify"&gt;Как видно, что статическое поле будет проинициализировано, хотя сам тип в приложении не используется. Практика показывает, что в большинстве случаев при отсутствии явного конструктора, JIT-компилятор вызывает инициализатор статических переменных &lt;b&gt;непосредственно перед вызовом метода, в котором используется эта переменная&lt;/b&gt;. Если раскомментировать статический конструктор класса &lt;b&gt;Singleton&lt;/b&gt;, то поведение будет именно таким, которое ожидает большинство разработчиков – инициализатор поля вызван не будет и при запуске приложения на экране будет только одна строка: &lt;b&gt;&lt;i&gt;“&lt;/i&gt;&lt;/b&gt;&lt;b&gt;&lt;i&gt;Starting &lt;/i&gt;&lt;/b&gt;&lt;b&gt;&lt;i&gt;Main…”&lt;/i&gt;&lt;/b&gt;.&lt;/p&gt;

&lt;p align="justify"&gt;&lt;font color="#526e66"&gt;&lt;b&gt;ПРИМЕЧАНИЕ
      &lt;br /&gt;&lt;/b&gt;Разработчик не может и не должен завязываться на время вызова статического конструктора. Если следовать «букве закона», то вполне возможна ситуация, когда в приведенном выше примере (без явного конструктора типа), переменная &lt;b&gt;Singleton.&lt;/b&gt;&lt;b&gt;S&lt;/b&gt; не будет проинициализирована при создании экземпляра класса &lt;b&gt;Singleton&lt;/b&gt; и при вызове статического метода, который &lt;b&gt;не использует поле&lt;/b&gt; &lt;b&gt;S&lt;/b&gt;, но будет проинициализирована при вызове статической функции, использующей поле &lt;b&gt;S&lt;/b&gt;. И хотя именно такое поведение исходно заложено в определение флага &lt;b&gt;beforeFieldInit&lt;/b&gt;, в спецификации языка C# специально говорится о том, что точное время вызова определяется реализацией. Так, например, при запуске приведенного выше исходного фрагмента (без явного статического конструктора) под .Net Framework 4, мы получим более ожидаемое поведение: &lt;b&gt;поле &lt;/b&gt;&lt;b&gt;S проинициализировано&lt;/b&gt; &lt;b&gt;не будет! &lt;/b&gt;Более подробно об этом можно почитать в дополнительных ссылках, приведенных в конце статьи.&lt;/font&gt;&lt;/p&gt;

&lt;h4&gt;&lt;b&gt;Статические конструкторы и взаимоблокировка&lt;/b&gt;&lt;/h4&gt;

&lt;p align="justify"&gt;Поскольку статический к
