tag:blogger.com,1999:blog-8596733192274108952.post2534254581143475700..comments2024-03-12T06:00:18.305+02:00Comments on Programming stuff: О синглтонах и статических конструкторахSergey Teplyakovhttp://www.blogger.com/profile/14300835272589262297noreply@blogger.comBlogger18125tag:blogger.com,1999:blog-8596733192274108952.post-45237251748664356772017-01-20T15:16:35.259+02:002017-01-20T15:16:35.259+02:00Как мне не хватало этой статьи, когда я бодался с ...Как мне не хватало этой статьи, когда я бодался с InvalidTypeException. Anonymoushttps://www.blogger.com/profile/16606064871919048698noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-67549426344928729102016-07-06T17:59:00.310+03:002016-07-06T17:59:00.310+03:00Ну, тут несколько моментов:
1. На экран ничего не ...Ну, тут несколько моментов:<br />1. На экран ничего не выводится, поскольку args.Length при запуске из VS равен 0.<br />2. У вас добавлен явный статический конструктор, который приводит к тому, что статический конструктор вызывается лишь в момент первого обращения к типу, но не раньше.<br />3. Достаточно убрать статический конструктор, чтобы увидеть, что статический конструктор будет вызываться даже в том случае, когда обрщение к синглтону будет происходить в недостижимом коде.Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-91971240971857767222016-07-06T12:33:45.138+03:002016-07-06T12:33:45.138+03:00namespace ConsoleApplication5
{
class Program
...namespace ConsoleApplication5<br />{<br /> class Program<br /> {<br /> static void Main(string[] args)<br /> {<br /> Console.WriteLine("Starting main...");<br /> if (args.Length == 1)<br /> {<br /> Console.WriteLine(Singleton.S);<br /> }<br /> Console.ReadLine();<br /> }<br /> }<br /><br /> class Singleton<br /> {<br /> public static string S = Echo("Field Init");<br /><br /> static Singleton()<br /> {<br /> S = Echo("abc");<br /> }<br /><br /> public static string Echo(string s)<br /> {<br /> Console.WriteLine(s);<br /> return s;<br /> }<br /> }<br />}<br /><br /><br /><br />.NET 4.5.2Anonymoushttps://www.blogger.com/profile/07884658301009574011noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-68529788967563414212016-07-05T23:25:12.180+03:002016-07-05T23:25:12.180+03:00Олег, а как выглядит приложение целиком?Олег, а как выглядит приложение целиком?Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-50637079355187972482016-07-05T23:00:14.799+03:002016-07-05T23:00:14.799+03:00не пойму почему, но сам статический конструктор, п...не пойму почему, но сам статический конструктор, похоже, вообще не вызывается. Ни дебаггером не поймать, ни Console.WriteLine ничего не выводит:<br /><br /> class Singleton<br /> {<br /> public static string S = Echo("Field Init");<br /><br /> static Singleton()<br /> {<br /> S = Echo("abc");<br /> }<br /><br /> public static string Echo(string s)<br /> {<br /> Console.WriteLine(s);<br /> return s;<br /> }<br /> }<br /><br />или он вызывается ранее, в какой-то момент сразу после компиляции и до запуска приложения?Anonymoushttps://www.blogger.com/profile/07884658301009574011noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-62266722058294002332011-08-19T13:29:37.295+03:002011-08-19T13:29:37.295+03:00>> Так, например, при запуске приведенного в...>> Так, например, при запуске приведенного выше исходного фрагмента (без явного статического конструктора) под .Net Framework 4, мы получим более ожидаемое поведение: поле S проинициализировано не будет!<br /><br />Проверил. Поле инициализируется. Как так?Kalifrikihttps://www.blogger.com/profile/05796112365985605082noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-80865995525270828332011-08-16T20:05:18.470+03:002011-08-16T20:05:18.470+03:00@Женя: да, совершенно верно.
Если говорить о базо...@Женя: да, совершенно верно.<br /><br />Если говорить о базовой и строгой гарантии исключений (раз уж тема такая пошла), то можно сказать, что синглтон на основе инициализатора статического поля обеспечивает базовую гарантию (состояние согласовано), а синглтон, на основе double-checked locking-а или на основе Lazy - обеспечивает строгую гарантию исключений, т.е. если создание экземпляра упадет, то мы "откатимся" в то состояние, которое было до создания экземпляра.<br /><br />Теперь по русски: при использовании статического конструктора, если он один раз упадет, то при последующем обращении мы не будем пытаться создать экземпляр снова, а лишь будем выбрасывать закешированное исключение. В случае с double-check locking-ом - мы будем пытаться создать экземпляр снова и снова каждый раз.Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-19897320748046798652011-08-16T12:57:14.012+03:002011-08-16T12:57:14.012+03:00Есть еще одна интересная особенность, почему навер...Есть еще одна интересная особенность, почему наверное не надо использовать статический конструктор. Если, кто-то, может быть после решит, что статический конструктор - отличное место для инициализации и добавит код, который будет генерить исключение - то все - ахтунг. Любая попытка обратиться к GetInstance() будет приводить к TypeInitializeException.eugenehttps://www.blogger.com/profile/00368111825500921630noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-82378993988494289262011-08-04T22:06:00.071+03:002011-08-04T22:06:00.071+03:00Олег, спасибо большое, но на самом деле указанную ...Олег, спасибо большое, но на самом деле указанную вами ссылку я привел:) Это и есть та статья Джона Скита, которая у меня значится под номером 4:<br />Jon Skeet. <a href="http://csharpindepth.com/Articles/General/Singleton.aspx" rel="nofollow">Implementing the Singleton Pattern in C#</a><br /><br />И если вы прочитаете хоть по своей ссылке, хоть по моей, то заметите, что основным минусом double-ckecked lock-а является невозможность использования этого паттерна в других языках, например в Java (причем до версии 1.5).<br /><br />Ну и последнее, эта заметка была не о синглтонах вообще, а о конкретной реализации синглтона и об особенностях статических конструкторов и инициализаторов полей.Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-4559383930866734432011-08-04T20:27:47.324+03:002011-08-04T20:27:47.324+03:00Странно, что не упомянута действительно правильная...Странно, что не упомянута действительно правильная реализация синглтона: http://www.yoda.arachsys.com/csharp/singleton.html Также об этом можно почитать в книге: http://www.amazon.com/3-0-Design-Patterns-Judith-Bishop/dp/059652773X/ref=sr_1_1?ie=UTF8&qid=1312478044&sr=8-1<br />А double-checking lock совсем даже не правильная. Об этом можно почитать по первой ссылке. Или, чтобы глубоко вникнуть в многопоточность и разобрать по косточкам почему это так, рекомендую: http://www.amazon.com/Concurrent-Programming-Windows-Joe-Duffy/dp/032143482X/ref=sr_1_1?s=books&ie=UTF8&qid=1312478185&sr=1-1Oleghttps://www.blogger.com/profile/06074969601667869308noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-73878640168985769392011-08-03T21:54:46.139+03:002011-08-03T21:54:46.139+03:00Борис, совершенно верно. Вызов подобного кода в ст...Борис, совершенно верно. Вызов подобного кода в статическом конструкторе гарантировано приводит к дедлоку. Просто в моем случае дедлок происходит только если код создания счетчиков производительности вызывается до функции Main. Поэтому это решает проблему (хотя риск дедлока все еще остается, поскольку ожидание освобождения именованого мьютекса, которое происходит при создании категориии счетчиков производительности все еще может приводить к дедлоку).Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-83331955422256253022011-08-03T10:49:37.098+03:002011-08-03T10:49:37.098+03:00А вот ситуация, где пустой статический конструктор...А вот ситуация, где пустой статический конструктор не помогает:<br /><br /> class Program<br /> {<br /> static void Main(string[] args)<br /> {<br /> Service service = new Service();<br /> }<br /> }<br /><br /> public class Service<br /> {<br /> private static readonly Service service = new Service();<br /><br /> public static Service Instance<br /> {<br /> get<br /> {<br /> return service;<br /> }<br /> }<br /><br /> static Service()<br /> {<br /> }<br /><br /> public Service()<br /> {<br /> Thread thread = new Thread(o => { });<br /> thread.Start();<br /> thread.Join();<br /> }<br /> }<br /><br />Выход из положения - использовать Lazy:<br /><br /> class Program<br /> {<br /> static void Main(string[] args)<br /> {<br /> Service service = new Service();<br /> }<br /> }<br /><br /> public class Service<br /> {<br /> private static readonly Lazy service = new Lazy(() => new Service());<br /><br /> public static Service Instance<br /> {<br /> get<br /> {<br /> return service.Value;<br /> }<br /> }<br /><br /> public Service()<br /> {<br /> Thread thread = new Thread(o => { });<br /> thread.Start();<br /> thread.Join();<br /> }<br /> }Борисhttps://www.blogger.com/profile/00315147989266456079noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-53644983091533539072011-08-02T21:58:10.267+03:002011-08-02T21:58:10.267+03:00@s_tristan: моя "правильная" реализация ...@s_tristan: моя "правильная" реализация синглтона заключается в наличии пустого статического конструктора, либо в использовании блокировки с двойной проверкой.Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-46880605576583276852011-08-02T12:20:00.626+03:002011-08-02T12:20:00.626+03:00Приведите, пожалуйста, Вашу "правильную"...Приведите, пожалуйста, Вашу "правильную" реализацию Синглтонаs_tristanhttps://www.blogger.com/profile/00632655152766079847noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-25155380179400220662011-08-02T08:46:20.875+03:002011-08-02T08:46:20.875+03:00@ony: говоря формально в паттерне синглтона даже о...@ony: говоря формально в паттерне синглтона даже о многопоточности речь не идет. Но поскольку многопоточные сценарии являются совершенно типовыми, то немногопоточная реализация считается тотальным муветоном.<br /><br />Тоже самое происходит и с отложенностью. Согласен, что это не есть обязательное требование. Но без отложенности мы получаем недетерменированное поведение. Сейчас поясню, что имею ввиду. <br />Чисто теоретически экземпляр синглтона может быть тяжелым, и может падать. Тогда, если эти падения будут происходить в непонятный момент времени, без непосредственного обращения к экземпляру, мы получим сложности в отладке. Апликуха грохнулась, а мы даже не понимаем почему, ведь у нас нет возможности определить причину падения, ни понять что мы делали.<br /><br />Все это делает "ленивость" синглтона таким же желательным свойством, как и "потокобезопасность".Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-23603048436373977012011-08-02T08:42:43.090+03:002011-08-02T08:42:43.090+03:00@eugene: Спасибо за идею. Я думаю, что как-нибудь ...@eugene: Спасибо за идею. Я думаю, что как-нибудь вернусь к этой теме и восполню сей пробел.Sergey Teplyakovhttps://www.blogger.com/profile/14300835272589262297noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-10282661391921203082011-08-01T23:17:38.125+03:002011-08-01T23:17:38.125+03:00...а во-вторых, эта реализация должна быть «отложе......а во-вторых, эта реализация должна быть «отложенной» (lazy), чтобы не создавать экземпляр (потенциально) дорого объекта раньше времени или в тех случаях, когда он вообще может не понадобиться...<br /><br /><b>Не должна</b> она быть отложенной, и <a href="http://en.wikipedia.org/wiki/Singleton_pattern" rel="nofollow">Wiki</a>, как ни странно, со мной согласна.<br />Самое главное в синглтоне это, что бы его время жизни покрывало моменты взаимодействия с ним. Можно - не значит нужно: <a href="http://en.wikipedia.org/wiki/Singleton_pattern#Implementation" rel="nofollow">"... although a singleton can be implemented as a static instance, it can also be lazily constructed, requiring no memory or resources until needed..."</a><br /><br />Необходимость смещения конструирования до по последнего момента вызвана тем что используют синглтон, в основном, что бы достичь этого, а не того что бы реализовать сам синглтон. Хотя на самом деле время жизни объекта синглтон может быть даже прерывистым, когда состояние выгружается куда-то, а потом восстанавливается или же может быть синглтон на каждый тред (актуально для кеширования).Anonymoushttps://www.blogger.com/profile/03159331377410025186noreply@blogger.comtag:blogger.com,1999:blog-8596733192274108952.post-10178911116919387942011-08-01T23:04:54.253+03:002011-08-01T23:04:54.253+03:00Хорошая статья. Недавно провел маленький опрос. Вы...Хорошая статья. Недавно провел маленький опрос. Выяснилось, что что такое класс-тип представление ОЧЕНЬ смутное. А то, что у него еще и конструктор есть... Когда вызывается было уже неактуально. Кстати, Сергей, наверное, надо тогда уж быть последовательным и рассказать про syncblockindex, на что указывает GetType(тип) и почему блокировки на типах - это плохо. Мне кажется это было бы очень в тему к двум предыдущим статьям?eugenehttps://www.blogger.com/profile/00368111825500921630noreply@blogger.com