C# — Проблема с программой (C#)


Содержание

C# Разработка

Блог о создании приложений на платформе .NET Framework

23 мая 2020 г.

Инструменты отладки: System.Diagnostics, Output window, DebuggerDisplay, Edit and continue, Immediate Window и т.д

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

Пространство имен System.Diagnostics

Если говорить о инструментарии, который уже имеется в составе .NET Framework, тогда речь пойдут о классах лежащих в пространстве имен System.Diagnostics.* . Из всего многообразия классов я выделю только те, которые помогут нам в разработке.

Класс взаимодействия с отладчиком (Debugger).

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

Метод Debugger.Launch()


Launch
работает так по правилам:

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

Простенький пример консольного приложения:

Запустив по F5 в студии вы ничего не заметите, но запусти в проводнике увидите диалог:

  • При отладке двух процессов, когда ваше приложение запускает второе приложение, которое быстро «что то делает» и закрывается. При этом вы не успеваете сделать Debug -> Attach to process.
  • Допустим вы являетесь разработчиком какого то расширения или add-on для какой то системы. При запуске система подгружаете ваш модуль в память и передает ему управление. Встает вопрос, как же теперь отлаживать ситуацию в момент запуска модуля? В этом случае добавляем в код метода Debugger.Launch() и в момент запуска получаем возможность отладить процесс запуска.
    P. S. Если вы пользуетесь Thread.Sleep(15000) + Attach to process, то мои вам соболезнования :)

Важно! Стоит помнить что даже в релизной сборке будет отображаться окно с отладчиками, поэтому не забывайте удалять Debugger.Launch(), когда надобность в нем отпадает.

Метод Debugger.Break()

  • Если отладчик подключен, то происходит остановка при вызове методе, сравнимая остановке на breakpoint .
  • Если же отладчик не подключен и вызывается метод Break(), тогда будет показано окно с выбором отладчика, но при этом закрыв его приложение продолжит работу.

Пример кода:

На практике использовал этот метод довольна редко, но было дело, что студия «глючила» и breakpoint слетали после перезапуска. Выходом был этот метод.

Output window и класс Debug.

  • Окно Output не присутствует на видимом рабочем пространстве. А находится где то снизу на вкладках, при этом программист может иногда поглядывать туда.
  • Окно Output всегда видно, но имеет настолько малый размер, что в нем ничего не понятно. При сборке что то мелькает, а на вопрос «Зачем тебе оно если ничего не понятно?» получал ответ «Что бы знать, что студия не подвисла и было на что поглядеть».
  • Последний вариант, программист активно пользуется им и читает, что пишется в окне. Но при этом в окне два scroll умещается 4-6 строчек, а при работе приложение внутри мелькает текст и бедняга программист пытается найти нужное место.
  1. Открепляем окно Ouput и перетаскиваем его в правую часть окна, закрепляя ее на фиксаторе как на картинке:
  • Далее окно Output полезно когда идет сборка проектов, но оно может при сборке не показываться, если не указать это явно в настройках. Поэтому идем Tools -> Options и там переходим в раздел «Projects and Solution» и проставляем галку «Show Ouput window when build starts«. И вуаля! Output при работе не занимает место, а при сборке и запуске он автоматически станет виден.
  • Последний штрих, это убрать лишние сообщения в Debug. Что бы это сделать запускаем приложение, открываем Output нажимаем правую кнопку по окну и снимаем галки с пунктов показанных на картинке:

    Теперь мы будем видеть вывод через Debug.Write и настроенный Debug.Trace.

    Настройка Log4net и TraceAppender к нему:

    • Первая ситуация, это особенности работы с UI, когда тебе требуется при открытом PopUp или при наличии фокуса в каком то контроле понять, какие события вызываются в CodeBehind иногда хватает breakpoint , но иногда какой то кейс наступает в случае, если мы не убираем фокус с формы, а как тогда отлаживать код, если фокус убирать нельзя? В исключительных ситуациях приходится использовать Debug.Write что бы увидеть точную синхронную последовательность вызовов, тем самым понять, что за чем следует.
    • Допустим у нас создается большое количество объектов, и нам необходимо узнать значение параметра в каждом из них. Вариант поставить точку останова и на каждом вызове смотреть и копировать значения — это слишком долго. Поэтому достаточно выводить в Debug Output тем самым имея возможность скопировать весь текст от туда и произвести нужный анализ.
    • Еще один из плюсов, именно Debug.Write — если вдруг забыли и не потерли вывод сообщений, то ваш спам не засветится в логе у заказчика.

    Атрибут DebuggerDisplayAttribute.

    Представьте жизненную ситуацию, у вас имеется ссылка на массив из объектов и вам надо как то посмотреть значение его полей. Для этого вам потребуется нажимать на стрелочку напротив каждого элемента, что бы увидеть внутренние поля которые вам нужны. Если у вас 3-5 объектов то это можно проделать вручную, но представьте, что там 100 объектов, а вам надо найти тот, у которого определенное значение поля. В итоге либо писать отдельный код который бы отфильтровал вам объекты и оставит нужный. Или можно использовать замечательный атрибут DebuggerDisplayAttribute, который, в режиме отладки, покажет вам всю необходимую для вас информацию об объекте.

    Постараюсь продемонстрировать работу на примере. Имеется код:

    После запуска в массиве people у вас будут лежать объекты Man со случайными значениями поля возраст. Допустим ваша задача узнать, есть ли объекты с именем «Sergei» в массиве. Запускаем приложение встаем на точке останова, выделяем people жмем «Quick Watch» и видим, что для того, что бы узнать имя нам потребуется у каждого объекта отрывать поля:

    По умолчанию отладчик выводит значение .ToString() объекта, он у нас не перегружен, поэтому ToString() выводит значение базового Object.ToString() выводящего имя класса. Поэтому я добавлю атрибут DebuggerDisplay к классу Man. Пример:

    Запустился и в «Quick Watch» видим совершенно другую картину:

    Как это работает?

    Очень просто, для понимания строку «Name = , Age = » стоит представлять как string .Format( «Name = <0>, Age = <1>» , Name, Age ), где на место фигурных скобок < >будет подставлено значение поля с именем Name и Age. Так же можно использовать выражение и даже вызывать методы, как если бы мы работали с объектом напрямую.

    Запустившись и увидим вот такую картинку:

    • Если свойство или поле вашего класса имеет значение NULL, а вы обратитесь к его свойству открыв окно отладчика, тогда будет NullReferenceException, который отобразится строкой. Поэтому, что бы DebuggerDisplay отображал все объекты корректно, стоит обрабатывать обращение к NULL полю.
    • Перед добавлением значения стоит помнить, что если обращение к свойству или методу приводит к запросу к БД или обращение к серверу, то и очевидно, что перед показом отладчик будет делать то же самое. Будьте к этому готовы.
    • Все изменения с полями\свойствами\объектами вызванными внутри форматированной строки останутся, как если бы вы сами это поменяли. Будьте внимательны!

    Перемещение вектора исполнения в Visual Studio.

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

    Правка кода в режиме отладки [Edit and continue].

    • «Запуск» приложения и проход до места работы с объектом.
    • Изучение значения одного из полей
    • Остановка приложения, с целью внести правки или дописать код для работы с объектом.
    • Повтор первого шага

    Либо еще одна ситуация:

    • Идет отладка кода
    • Мы проваливаемся в какой то метод и замечаем, что в нем нужно не забыть сделать какую то несущественную правку.
    • При этом отладка основного сценария продолжается, а когда отладка завершится, не факт что сможешь вспомнить, что хотел ещё подправить
    1. Ставлю точку останова в месте, где интересующий меня объект доступен по ссылке.
    2. Запускаю решение и дохожу до точки останова
    3. Затем уже в месте, где собираюсь писать код для работу с этим объектом начиная подсматриваю по мере необходимости его поля.

    Палочка выручалочка «Immediate Window».

    За всё время пока я занимаюсь программированием под .NET Framework я ни разу не видел, что бы кто то при мне пользовался «Immediate Window» в Visual Studio, поэтому если ты слышишь про него первый раз, то обязательно добавь его на рабочее пространство и используй! А вот как, я расскажу прям сейчас.

    Представьте ситуации, мы стоим на точке останова и вдруг:

    • Появляется необходимость вызвать какой то метод доступный из текущего места.
    • У локальной переменной хотим заменить значение БЕЗ перезапуска приложения
    • Посмотреть результат выполнения
    • Иными словами хочется вставить и исполнить какую то временную инструкцию.

    Думаете это не возможно? Вы ошибаетесь — выход это «Immediate Window». Встав на точку останова вы можете писать не просто выражения, вызывать методы, но и менять значения переменных локальных и глобальных. При этом данный код будет выполняться отдельно от вашего исходного текста программы.

    • В самом окне работает IntelliSence поэтому проблем с именами быть не должно.
    • Само окно в случае, если введенное выражение возвращает какой то сложный объект, используется продвинутый Formatter, который покажет в читабельном виде все его поля.
    • Не обязательно указывать «;» после каждого выражения.

    • Очень удобно использовать когда надо вывести все поля в читабельном виде.
    • Стрелками «Вверх\вниз» можно подставить ранее введенные выражения.
    • Можно задавать значения локальных переменных

    Остановка по требованию или Condition breakpoints.

    Допустим есть код:

    Наша цель, когда i будет равно 500 остановиться и узнать полученное значение от суммы со случайным числом. Если вы садомазахист, то можете поставить точку останова и 500 раз нажимать F5. Но лучшим решением будет поставить точку останова в начале метода, выбрать «Conditions. «:

    В появившемся окне в поле «условие» вводим «i == 500» и если условие указано без ошибок, то по нажатию на Enter красная точка поменяет свой вид на точку с плюсом.

    Запускаем программу и видим, что мы остановились на месте, когда значение i стало 500.

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

    Удалённый отладчик Remote Debugger.

    Допустим вам в какой то ситуации потребовалось отладить вашу программу на другом компьютере. Вы скачиваете на нее исходный код, устанавливаете студию и начинаете отладку. В таком случае есть возможность работать в студии, которая стоит на вашем компьютере, а к удалённому компьютеру, для отладки, подключаться через Remote Debugger Tools.

    Важно знать! Что нужно ставить Remote Debugger Tools для версии вашей студии, обратную совместимость версии не гарантируется. Поэтому если у вас к примеру Visual Studio 2012, тогда нужно искать Remote Debugger для 2012.

    Всю настройку описывать не буду, по адресу Remote Debugging можно получить всю интересующую информацию.

    Отладка без полного исходного кода приложения

    Ситуация, когда требуется у заказчика отладить какой то код или модуль, при этом у вас есть только RDP доступ к рабочей машине и все остальные порты закрыты, и никакой Remote Debugger не поможет. В этой ситуации хочешь не хочешь придётся поставить Visual Studio, благо у неё есть триальный период, но встаёт вопрос, что делать с исходными кодами? Переносить весь код вашего решения, за которое заказчик платит деньги как то стрёмно, но есть выход. В случае если требуемое место находися в отдельном модуле тогда можно ограничиться переносом его и далее делая Debug > Attach to process и расставив точки останова можно отладить его.Но допустим у вам известен файл, который надо отладить в таком случае не надо копировать весь исходный код модуля. Достаточно скопировать только файл MyModule.cs на стенд заказчика, отрываем его студией цепляемся к работающиму процессу ставим точки останова и отлаживаемся!

    При этом требуется две вещи:

    1. Сборка в работающем приложении должна быть /debug
    2. *.pdb файл должен находиться рядом с отлаживаемым модулей и должен быть актуальным.

    Теперь расскажу подробнее на примере:

    • Допустим у нас есть файл BusinessModule.cs, который содержит код и требует отладки

      Для понимания, что произойдёт привожу код Program.cs:

      Теперь я смогу запустить приложение и по нажатию любой кнопки произойдёт вызов DoSomething() нашего файла. Теперь я для правдоподобности переношу файл BusinessModule.cs в другой раздел жёсткого диска.

      Затем отрываем файл в Visual Studio. Если вы хотите локально проверить, то закройте студию в которой этот файл уже отрыт, иначе эта студию будет открываться первой. В результате мы видим отрытый файл без .sln и других файлов проекта.

    • Всё готово для теста, я в папке Bin запускаю ранее скомпилированный executable file. В студии захожу Debug > Attach to process выбираю запущенный процесс жму Attach ставлю точку останова, в приложение нажимаю любую клавишу и видим, что мы благополучно встали на точку останова, при этом у нас отрыт один лишь файл:

    Таким образом, мы относительно безопасно можем отладить участок программы без «засветки» всего исходного кода.

    Преимущества и недостатки C# — Учим Шарп #1

    Опубликовано shwan в 06.11.2020

    Я хочу обсудить с тобой один очень важный вопрос, который достаточно часто упускают из внимания: действительно ли тебе нужно учить язык C#?

    Что бы тебе ни рассказывали на всевозможных онлайн курсах, книгах, тренингах и конференциях, о том, как можно стать программистом за 21 день – все это ложь, п*дежь и провокация.

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

    Поэтому, я предлагаю тебе предварительно рассмотреть основные преимущества и недостатки языка C#, перспективы развития, сферы применения и уровни зарплатных ожиданий. Это может помочь тебе понять действительно ли ты правильно инвестируешь свое время. А время – это самый ценный ресурс, запомни это.

    История языка C#

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

    Данный язык создан всеми так горячо любимой корпорацией Зла Microsoft в 2000 году. Он очень многое унаследовал от своих родителей (С++ и Java), но и привнес нового.

    Возможной версией использования символа хештега принято считать наследование плюсиков от предков, примерно следующим образом: C → C++ → C++++(C#), потому что символ «#» при хорошем воображении можно получить путем объединения 4-х знаков «+».

    Язык активно развивается. Регулярно выходят новые версии C#, которые добавляют новые синтаксические конструкции в язык, а также увеличивают его быстродействие и надежность.

    Преимущества C#

    Данный язык использует объектно-ориентированный подход к программированию во всем. Это означает, что тебе нужно будет описывать абстрактные конструкции на основе предметной области, а потом реализовывать между ними взаимодействие. Данный подход пользуется большой популярностью, потому что позволяет не держать в голове всю информацию, а работать по принципу черного ящика: подал входные данные -> МАГИЯ -> PROFIT.

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

    Еще стоит упомянуть, что все это работает на базе платформы .NET Framework. Что это означает? Для многих непосвященных, это просто какая-то приблуда, которую нужно установить на комп, чтобы программа запустилась, но дело обстоит значительно глубже. Написанный тобой код на языке C# транслируется в промежуточный язык (IL), который в свою очередь уже преобразуется в машинный код на твоем компьютере прямо во время выполнения приложения (JIT). Спрашивается, зачем это все? А суть в том, что ты можешь пилить со своим другом Васей на разных языках один и тот же проект и ни одному из вас не придется переучиваться. Но я никогда не видел, чтобы это реально использовали на практике. Но это еще не все. Так как окончательная компиляция из промежуточного кода выполняется в живую на твоей конкретной машине, то возможно увеличение производительности за счет использования специфических команд именно твоего процессора.

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

    Ты просто скачиваешь нужное тебе решение из nuget и начинаешь его использовать. В большинстве своем они бесплатны. Сюда же можно отнести большое количество обучающего и справочного материала. Практически на любой свой вопрос ты сможешь найти ответ на стековерфлоу. Ну а на крайний случай всегда можешь спросить в моем телеграм чате для программистов.

    Немаловажно наличие хороших инструментов разработки, и здесь все очень хорошо. Абсолютное большинство разработчиков используют интегрированную среду разработки Visual Studio, которая предоставляет over9000 возможностей, использовать которые ты конечно же не будешь.

    Но IDE действительно хороша, к тому же имеется ее полнофункциональная бесплатная версия Community.

    Еще к плюсам можно отнести строгую типизацию, которая позволяет защититься от дурака, и не так давно появившаяся кросспратформенность в .NET Core (да-да, мелкомягкие потихоньку захватывают линукс).

    Недостатки C#

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

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

    .NET использует концепцию JIT-компиляции. Это означает, что программа будет скомпилирована в машинные коды по мере необходимости прямо во время работы приложения. С одной стороны, это конечно прикольно, но при первом запуске возможны весьма серьезные тормоза.

    C# не является повсеместно распространенным языком. Большинство программистов сосредоточены в коммерческой Enterprise сфере, что накладывает весьма серьезные ограничения на поиск работы в небольших городах, где кроме Delphi или PHP ничего жизни не видели. К тому же, как бы то ни было, C# в первую очередь ассоциируется с Windows. Вряд ли в обозримом будущем что-то изменится и Винда все также будет продолжать доминировать на рынке, но все же небольшой риск остается, особенно учитывая недавние фейлы с обновлениями в Windows 10.

    Перспективы развития C#

    C# входит в первую пятерку самых популярных языков программирования на 2020 год. Он используется во многих крупных компаниях, а также и в небольших стартапах. Сейчас компания Microsoft делает большой упор на развитие универсальности и кросспалтформенности для этого языка. Уже сейчас с его помощью можно разрабатывать практически любой тип приложений.

    Компания Microsoft остается одной из крупнейших IT компаний мира, а C# ее флагманский язык программирования, который постоянно развивается и впитывает в себя все новые возможности. Поэтому в обозримом будущем проблем у данного языка возникнуть не должно.

    Сферы применения языка C#

    В этой области C#, наверное, впереди планеты всей. Хочешь разрабатывать обычные приложения для компьютера – пожалуйста, стандартные WinForms Application и консоль тебе в помощь. Хочешь такие же, но покрасивее? – используй WPF. И специальные приложения для магазина в Windows Store тоже. Веб-приложения? – Легко ASP.NET всегда придет на помощь. На Linux? – тоже не вопрос, .NET Core уже здесь. Мобильное приложение? – Xamarin сделает сразу под все платформы. Хочешь написать игру? – движок Unity показывает себя очень даже неплохо, и при этом также адаптирует игру под различные платформы. Хочешь приблизить апокалипсис с восстанием машин и создаешь искусственный интеллект? – есть целая платформа с кучей инструментов для этого Microsoft AI Platform. Также и для компьютерного зрения и ботов. Я вообще с трудом могу придумать пример того, что невозможно реализовать на C#. Я где-то встречал даже операционную систему написанную на шарпе. Поэтому в этой области все хорошо.

    Зарплаты разработчиков C#

    Здесь все в целом неплохо. По данным на 2020 год, C# явно не является самым высокооплачиваемым языком, но и не самый низкооплачиваемый тоже. Среднее значение зарплаты для данного языка в России около 90к рублей. Это весьма неплохой результат, но бесспорно есть и более дорогие языки. В целом, уровень заработной платы намного больше зависит от прямоты рук и уровня знаний, поэтому не так важно на каком языке писать, главное делать это хорошо. Но данная цифра задает примерный уровень для Middle разработчика в среднем по России. От этого уже можно отталкиваться.

    Цукерберг рекомендует:  Android - Как начать программировать под андроид с полного нуля

    Итоги

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

    В целом это перспективный язык, достаточно простой для освоения начинающим. Но следует помнить, что существует множество альтернатив, которые лучше подходят под некоторые задачи. Например, могу порекомендовать языки C++, Java, Python.

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

    Основная идея C# — это универсальность. Ты можешь реализовать все на одном языке и для любой платформы, но с большой долей вероятности это будет работать медленнее, чем разработанное специально под конкретную платформу.

    Он неплохо подходит как для начинающих в качестве первого языка, так и для реализации крупных коммерческих проектов.

    На этом мы заканчиваем наш первый урок. Подписывайтесь на мои социальные сети: Вконтакте, Телеграм, YouTube и Дзен. Ну а еще есть специальный закрытый чат, для изучающих C# по моему курсу. С вами был Вадим. Пока!

    Главное в .NET — Обработка исключение на C#

    By Марк Михейлис | November 2015

    Добро пожаловать в новую рубрику «Главное в .NET». В этой рубрике вы сможете следите за всем, что творится в мире Microsoft .NET Framework, будь то достижения в C# vNext (в настоящее время C# 7.0), усовершенствования во внутреннем устройстве .NET или события на фронте Roslyn и .NET Core (например, перевод MSBuild в статус ПО с открытым исходным кодом).

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

    Я живу в Спокане (штат Вашингтон), где являюсь «главным корифеем» в консалтинговой компании сегмента «high-end», которая называется IntelliTect (IntelliTect.com). IntelliTect специализируется на решении трудных задач и великолепно справляется с ними. Я был Microsoft MVP (в настоящее время в области C#) в течении 20 лет и восемь из них занимал должность регионального директора Microsoft. Сегодня я открываю рубрику обсуждением обновленных правил обработки исключений.

    В C# 6.0 включили два новых средства обработки исключений. Во-первых, появилась поддержка условий исключения (exception conditions) — возможность предоставлять выражение, которое отфильтровывает исключение, предотвращая его попадание в блок catch до раскрутки стека. Во-вторых, введена поддержка асинхронности в блоке catch — того, что было немыслимо в C# 5.0, хотя именно тогда в язык добавили асинхронность. Кроме того, в последних пяти версиях C# и соответствующих выпусках .NET Framework было много других изменений, которые в некоторых случаях достаточно значимы, чтобы потребовать правок в правилах кодирования на C#. В этом выпуске рубрики я дам обзор этих изменений и представлю обновленные правила кодирования, относящиеся к обработке исключений — их захвату.

    Захват исключений: обзор

    Как хорошо понятно, генерация исключения определенного типа позволяет механизму захвата (catcher) использовать сам тип исключения для идентификации проблемы. Иначе говоря, не обязательно захватывать исключение и применять выражение switch к сообщению об исключении, чтобы определить, какое действие надо предпринять в связи с этим исключением. Вместо этого C# поддерживает множество блоков catch, каждый из которых нацелен на исключение конкретного типа, как показано на рис. 1.

    Рис. 1. Захват исключений разных типов

    Когда возникает исключение, управление передается первому блоку catch, способному обработать это исключение. Если с try сопоставлено более одного блока catch, близость совпадение определяется цепочкой наследования (предполагая отсутствие условия исключения C# 6.0), и обработка исключения передается первому catch в этой цепочке. Например, в коде на рис. 2 генерируется исключение типа System.Exception, и оно обрабатывается вторым блоком catch, так как System.InvalidOperationException в конечном счете наследует от System.Exception. Поскольку InvalidOperationException наиболее близко совпадает со сгенерированным исключением, оно будет захвачено блоком catch(InvalidOperationException. ), а не catch(Exception. ), если бы таковой блок был в этом коде.

    Блоки catch должны появляться в порядке (вновь предполагая отсутствие условия исключения в C# 6.0) от наиболее специфичных до наиболее универсальных, чтобы избежать ошибки при компиляции. Например, добавление блока catch(Exception. ) до любого другого приведет к ошибке при компиляции, поскольку все предыдущие исключения наследуют от System.Exception в той или иной точке цепочки наследования. Также заметьте, что именованный параметр для блока catch не обязателен. По сути, заключительный catch, к сожалению, допускается даже без указания типа параметра.

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

    Сценарий 1 Захваченное исключение не в полной мере идентифицирует проблему, из-за которой возникло это исключение. Например, при вызове System.Net.WebClient.DownloadString с допустимым URL исполняющая среда может сгенерировать System.Net.WebException, если нет сетевого соединения; то же самое исключение генерируется при указании несуществующего URL.

    Сценарий 2 Захваченное исключение включает закрытые данные, которые не должны раскрываться выше по цепочке вызовов. Например, в очень ранней версии CLR v1 (фактически в версии pre-alpha) было исключение, сообщающее нечто вроде «Security exception: You do not have permission to determine the path of c:\temp\foo.txt» («Исключение защиты: у вас нет прав на определение пути c:\temp\foo.txt»).

    Сценарий 3 Тип исключения слишком специфичен для обработки вызвавшим кодом. Например, исключение System.IO (вроде UnauthorizedAccessException, IOException, FileNotFoundException, DirectoryNotFoundException, PathTooLongException, NotSupportedException или SecurityException, ArgumentException) возникает на сервере при вызове веб-сервиса для поиска почтового кода ZIP.

    При повторной генерации другого исключения обратите внимание на тот факт, что это может привести к потере исходного исключения (так делается преднамеренно только в сценарии 2). Чтобы предотвратить это, указывайте в свойстве InnerException исключения-оболочки, которое обычно устанавливается через конструктор, захваченное исключение, если только это не раскрывает закрытые данные, не подлежащие раскрытию выше в цепочке вызовов. Делая так, вы сохраните доступность трассировки исходного стека.

    Если вы не указываете вложенное исключение, но по-прежнему присваиваете экземпляр исключения после выражения throw, позиция в трассировке стека будет установлена на этот экземпляр исключения. Даже если вы повторно сгенерируете ранее захваченное исключение, трассировка стека которого уже присвоена, оно будет сброшено.

    Второй вариант при захвате исключения — определять, что вы фактически не в состоянии должным образом обработать его. В этом случае вы захотите повторно сгенерировать точно такое же исключение, отправив его следующему обработчику вверх по цепочке вызовов. Блок catch(InvalidOperationException. ) на рис. 1 как раз и демонстрирует этот вариант. В выражении throw не указана идентификация генерируемого им исключения (присутствует только ключевое слово throw), хотя экземпляр исключения (exception) появляется в области видимости этого блока catch и его можно было бы сгенерировать повторно. Генерация специфического исключения привела бы к обновлению всей информации стека для соответствия новой позиции throw. В итоге вся информация стека, указывающая место вызова, где возникло исходное исключение, была бы утрачена, что сильно затруднило бы диагностику проблемы. Так что, определив, что данный блок catch не может полноценно обработать исключение, это исключение следует генерировать повторно, используя пустое выражение throw.

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

    Генерация существующих исключений без замены информации стека

    В C# 5.0 был добавлен механизм, позволяющий генерировать ранее сгенерированное исключение, не теряя информации трассировки стека в исходном исключении. Это дает возможность повторно генерировать исключения, например, даже извне блока catch, а значит, без использования пустого выражения throw. Хотя потребность в этом возникает весьма редко, в некоторых случаях исключения обертываются или сохраняются до тех пор, пока поток выполнения программы не выйдет из блока catch. Так, многопоточный код мог бы обертывать исключение в AggregateException. .NET Framework 4.5 предоставляет класс System.Runtime.ExceptionServices.ExceptionDispatchInfo специально для этого случая, для чего используются его статический метод Capture и метод экземпляра Throw. Рис. 2 демонстрирует повторную генерацию исключения без сброса информации трассировки стека или применения пустого выражения throw.

    Рис. 2. Использование ExceptionDispatchInfo для повторной генерации исключения

    В случае метода ExeptionDispatchInfo.Throw компилятор не интерпретирует его как выражение возврата в том же стиле, в каком это могло бы быть сделано для обычного выражения throw. Например, если бы сигнатура метода возвращала значение, но на самом деле никакого значения по пути кода с ExceptionDispatchInfo.Throw не возвращалось бы, компилятор выдавал бы ошибку, указывающую на отсутствие возвращенного значения. Иногда разработчикам приходится следовать ExceptionDispatchInfo.Throw с выражением return, хотя такое выражение никогда не будет выполнено — вместо этого будет генерироваться исключение.

    Захват исключений в C# 6.0


    Универсальное правило обработки исключений — избегать захвата тех исключений, которые вы не можете полноценно обработать. Однако, поскольку выражения catch до появления C# 6.0 могли фильтровать исключения лишь по их типу, возможность проверки данных и контекста исключения до раскрутки стека в блоке catch требовали, чтобы этот блок был обработчиком до анализа исключения. К сожалению, после принятия решения о невозможности обработки исключения писать код, который позволяет другому блоку catch в том же контексте обработать исключение, было весьма затруднительно. А повторная генерация того же исключения вынуждает инициировать двухпроходный процесс обработки исключения, который сначала доставляет исключение выше по цепочке вызовов до тех пор, пока не найдется блок, способный его обработать, а затем раскручивает стек вызовов для каждого фрейма в стеке между исключением и позицией захвата.

    Вместо того чтобы после генерации исключения раскручивать стек вызовов в блоке catch лишь для повторной генерации исключения из-за того, что при дальнейшем анализе стала понятной невозможность его полноценной обработки, очевидно, было бы предпочтительнее вообще не захватывать это исключение. Начиная с C# 6.0, в блоках catch доступно дополнительное условное выражение. Вместо подбора блока catch только по типу исключения в C# 6.0 введена поддержка проверки по условию. Выражение when позволяет вам предоставлять булево выражение, которое дополнительно фильтрует блок catch и определяет, что он способен обработать исключение, только если условие дает true. Блок System.Web.HttpException на рис. 1 демонстрирует это с помощью оператора сравнения на равенство (equality comparison operator).

    Интересный результат условия исключения в том, что, когда указывается это условие, компилятор не принуждает к расстановке блоков catch в порядке цепочки наследования. Например, блок catch для типа System.ArgumentException с сопутствующим условием исключения теперь может появляться до более специфического блока catch для типа System.ArgumentNullException, хотя последний наследует от первого. Это важно, так как позволяет писать специфическое условие исключения, которое сопоставлено универсальному типу исключения, следующему за более специфическим типом исключения (с условием исключения или без него). Поведение в период выполнение остается согласованным с более ранними версиями C#: исключения захватываются первым совпадающим блоком catch. Просто теперь вопрос о том, можно ли считать какой-то блок catch совпадающим, решается на основе комбинации типа и условия исключения, а компилятор вводит порядок относительно только блоков catch, не имеющих условий исключения. Например, catch(System.Exception) с условием исключения может появляться до catch(System.ArgumentException) с условием исключения или без него.

    Однако, как только появляется catch без условия исключения, более специфический блок catch [скажем, catch(System.ArgumentNullException)] может быть блокирован, даже если в нем есть условие исключения. Это оставляет программисту свободу в кодировании условий исключений, которые потенциально могут появляться не по порядку, — при этом более ранние условия исключения, захватывающие исключения, предназначенные для более поздних условий, что потенциально может даже сделать последних непреднамеренно недостижимыми. В конечном счете порядок ваших блоков catch аналогичен тому, как вы располагали бы выражения if-else. Когда условие удовлетворяется, все остальные блоки catch игнорируются. Однако в отличие от условий в выражениях if-else все блоки catch должны включать проверку типа исключения.

    Обновленные правила обработки исключений

    Пример оператора сравнения на рис. 1 тривиален — условие исключения не обязательно должно быть столь простым. Вы могли бы, например, вызывать какой-то метод для проверки условия. Единственное требование в том, что выражение должно быть предикатом — возвращать булево значение. Иначе говоря, вы можете фактически выполнять любой код из цепочки вызовов захвата исключения. Это открывает возможность полного предотвращения повторного захвата и генерации того же исключения; по сути, вы можете достаточно сузить контекст до захвата исключения и захватывать его только в том случае, если оно действительно будет обработано. Таким образом, правило избегать захвата исключений, которые вы не в состоянии полностью обработать, становится реальностью. Любая проверка условий, окружающая пустое выражение throw, вероятно, может служить признаком плохого кода (code smell), и этого следует избегать. Подумайте о добавлении условия исключения вместо использования пустого выражения throw, кроме случая сохранения измененяемого состояния (volatile state) перед завершением процесса.

    Вместе с тем разработчики должны ограничивать блоки условных выражений проверкой только контекста. Это важно, поскольку, если само условное выражение генерирует исключение, это новое исключение игнорируется, а условие интерпретируется как false. По этой причине вы должны избегать генерации исключений в условных выражениях.

    Универсальный блок catch

    C# требует, чтобы любой объект, генерируемый кодом, был производным от System.Exception. Однако это требование не универсально для всех языков. Так, C/C++ позволяет генерировать объект любого типа, в том числе управляемые исключения, не производные от System.Exception, или даже элементарные типы вроде int или string. Начиная с C# 2.0, все исключения — производные они от System.Exception или нет — будут передаваться в C#-сборки как производные от System.Exception. Результат заключается в том, что блоки catch для System.Exception будут захватывать все «достоверно обрабатываемые» исключения, не захваченные предшествующими блоками. Однако до C# 1.0, если исключение, не производное от System.Exception, генерировалось при вызове какого-то метода (находящего в сборке, написанной не на C#), то оно не захватывалось блоком catch(System.Exception). По этой причине C# также поддерживает универсальный блок catch (catch< >), который теперь ведет себя идентично блоку catch(System.Exception exception) с тем исключением, что в нем не указывается ни тип, ни имя переменной. Недостаток такого блока в том, что у вас нет экземпляра исключения, к которому вы могли бы обратиться, и нет способа узнать, какие действия следовало бы предпринять. Нельзя даже запротоколировать это исключение или распознать маловероятный случай, где такое исключение безопасно.

    На практике вы должны избегать блока catch(System.Exception) и универсального блока catch (далее обобщенно называемых блоком catch для System.Exception), кроме как под предлогом «обработки» исключения простым его протоколированием перед завершением процесса. Следуя универсальному правилу захватывать только те исключения, которые вы можете обработать, было бы слишком самонадеянным писать код, для которого программист объявляет, что этот catch может обрабатывать любые возможные исключения. Во-первых, попытка перечислить всевозможные исключения (особенно в теле Main, где находится самое большое количество выполняемого кода и где контекст скорее всего минимален) — задача неподъемная, кроме как в простейших программах. Во-вторых, существует уйма исключений, которые могут генерироваться неожиданно для вас.

    До C# 4.0 существовал третий набор исключений из-за поврежденного состояния, при которых программу, как правило, нельзя было восстановить хотя бы в принципе. Этот набор не столь значим, начиная с C# 4.0, но catch(System.Exception) (или универсальный блок catch) на самом деле не будет захватывать такие исключения. (С технической точки зрения, метод можно дополнить System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions, чтобы захватывать даже эти исключения, но вероятность того, что вы сумеете в достаточной мере обработать их, крайне низка. Подробнее на эту тему см. по ссылке bit.ly/1FgeCU6.)

    Одна техническая деталь, которую стоит отметить в исключениях из-за поврежденного состояния, заключается в том, что при генерации в период выполнения они передаются только через блоки catch для System.Exception. Явно сгенерированное исключение из-за поврежденного состояния, такое как System.StackOverflowException или другое System.SystemException, будут фактически захвачены. Однако генерация такого исключения будет грубейшей ошибкой и на самом деле поддерживается только для обратной совместимости. Современные правила требуют, чтобы вы не генерировали какое-либо из этих исключений (в том числе System.StackOverflowException, System.SystemException, System.OutOfMemoryException, System.Runtime.InteropServices.COMException, System.Runtime.InteropServices.SEHException и System.ExecutionEngineException).

    Подведем итог. Избегайте использования блока catch для System.Exception, если только он не должен обработать исключение с помощью какого-то кода очистки и запротоколировать факт этого исключения до повторной генерации или корректного завершения приложения. Например, если бы блок catch мог успешно сохранять любые изменяемые данные (на что в любом случае не стоит полагаться, так как они тоже могут быть повреждены) до закрытия приложения или повторной генерации исключения. При возникновении ситуации, в которой приложение следует завершить, потому что продолжать выполнение небезопасно, код должен вызывать метод System.Environment.FailFast. Используйте System.Exception и универсальные блоки catch только для протоколирования исключения перед завершением приложения.

    Заключение

    В этой статье я изложил обновленные правила обработки исключений в части их захвата. Эти обновления вызваны усовершенствованиями, внесенными в C# и .NET Framework в последних нескольких версиях. Несмотря на тот факт, что некоторые правила новые, многие остаются прежними. Вот краткая сводка правил захвата исключений:

    • избегайте захвата исключений, которые вы не в состоянии полноценно обработать;
    • избегайте скрытия (отбрасывания) исключений, которые вы не полностью обрабатываете;
    • используйте throw для повторной генерации исключения, а не throw внутри блока catch;
    • указывайте в свойстве InnerException исключения-оболочки захваченное исключение, если только это не приводит к раскрытию конфиденциальных данных;
    • подумайте о применении выражения условия вместо повторной генерации исключения после захвата исключения, которое вы не можете обработать;
    • избегайте генерации исключений из условного выражения исключения;
    • будьте осторожны при повторной генерации других исключений;
    • используйте System.Exception и универсальные блоки catch только в редких случаях — при необходимости запротоколировать исключение перед завершением приложения;
    • избегайте отчета об исключении или его протоколирования ниже по стеку вызовов.

    Подробный анализ каждого из этих правил вы найдете на сайте itl.tc/ExceptionGuidelinesForCSharp. В следующей рубрике я собираюсь уделить больше внимания правилам генерации исключений. А пока будет достаточно сказать, что адресатом исключения является программист, а не конечный пользователь программы.

    Заметьте, что многое из этого материала взято из следующего издания моей книги «Essential C# 6.0 (5th Edition)» (Addison-Wesley, 2015), которая доступна сейчас на itl.tc/EssentialCSharp.

    Марк Михейлис*(Mark Michaelis) — учредитель IntelliTect, где является главным техническим архитектором и тренером. Почти два десятилетия был Microsoft MVP и региональным директором Microsoft с 2007 года. Работал в нескольких группах рецензирования проектов программного обеспечения Microsoft, в том числе C#, Microsoft Azure, SharePoint и Visual Studio ALM. Выступает на конференциях разработчиков, автор множества книг, последняя из которых — «Essential C# 6.0 (5th Edition)». С ним можно связаться в Facebook (facebook.com/Mark.Michaelis), через его блог (IntelliTect.com/Mark), в Twitter (@markmichaelis) или по электронной почте mark@IntelliTect.com.*

    Выражаю благодарность за рецензирование статьи экспертам Кевину Босту (Kevin Bost), Джейсону Питерсону (Jason Peterson) и Мэдсу Торгерсону (Mads Torgerson).

    c# — Проблема с программой просмотра отчетов и сеансами

    Я использую С# MVC 4 и встроил страницу aspx для просмотра отчетов для вывода отчетов из SSRS. Я использую Sql Server 2008R2, Microsoft.ReportViewer.WebForms версии 11.0.

    Во-первых, проблема, с которой я столкнулся,

    Я использую переменные сеанса в проекте для хранения значений относительно сайта. Они не имеют ничего общего с SSRS, например UserId.

    В web.config у меня есть

    Примечание. Тайм-аут для этого сценария тестирования смехотворно высок.

    Simarlarly Я обновил ConfigurationInfo в базе данных ReportServer для ключевого SessionTimeout до 60 (опять-таки нелепое значение для тестирования).

    Мой код ReportViewer выглядит следующим образом:

    Проблема, с которой я сталкиваюсь

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

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

    К моей сессии добавляется ключ сеанса

    На этом этапе все нормально, и я даже закрываю страницу отчета.

    В этот момент я жду минуту, которая будет означать, что сеанс истекает в ReportServerTempDB.

    Затем я перехожу к странице, действие которой использует значение HttpContext.Current.Session для значения. Опять же, это действие не имеет ничего общего с отчетами. Однако, когда он пытается получить ключ, я получаю следующую ошибку

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

    Прокомментируйте, если вам нужна дополнительная информация, и я обновлю вопрос.

      3 1
    • 26 мар 2020 2020-03-26 17:54:54
    • Kamal

    1 ответ

    Я столкнулся с тем же вопросом. Похоже, что когда объекты, которые отправляет ReportViewer в сеанс, получают доступ, они пытаются выполнить ping сервер отчетов. Если срок действия отчета истек, возникает запрос ReportServerException, ожидаемое поведение, когда пользователь все еще просматривает отчет с истекшим сроком действия, но не тогда, когда он больше не находится на этой странице.

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

    Хотя мы не можем получить список ключей, у нас есть доступ к числу записей в сеансе. Используя эту информацию, мы можем пройти через каждый элемент в сеансе и определить, является ли это одним из связанных с ReportViewer, и удалить его.

    Блок catch помогает удалить уже прошедшие отчеты (и, таким образом, вызывать исключение при доступе), в то время как проверка типа в операторе if приведет к удалению отчетов, которые еще не истекли.

    Цукерберг рекомендует:  Тест по PHP. Средний уровень

    Я рекомендую вызывать это из события onunload страницы, содержащей ваш ReportViewer.

    C # — Обработка исключений

    Исключением является проблема, возникающая во время выполнения программы. Исключение AC # — это ответ на исключительное обстоятельство, которое возникает во время работы программы, например попытка деления на ноль.

    Исключения обеспечивают способ передачи контроля из одной части программы в другую. Обработка исключений C # построена на четырех ключевых словах: try , catch , finally и throw .

    • try — блок try идентифицирует блок кода, для которого активируются определенные исключения. За ним следует один или несколько блоков catch .
    • catch — программа выхватывает исключение с обработчиком исключений в месте в программе, где вы хотите справиться с этой проблемой. Ключевое слово catch указывает на улавливание исключения.
    • finally — блок finally используется для выполнения заданного набора операторов, независимо от того, выбрано или не выбрано исключение. Например, если вы открываете файл, он должен быть закрыт независимо от того, создано ли исключение или нет.
    • throw — программа выдает исключение, когда возникает проблема. Это делается с использованием ключевого слова throw .

    Синтаксис

    Предполагая, что блок вызывает исключение, метод выбирает исключение, используя комбинацию ключевых слов try и catch . Блок try / catch помещается вокруг кода, который может генерировать исключение. Код внутри блока try / catch называется защищенным кодом, а синтаксис использования try / catch выглядит следующим образом:

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

    Классы исключений в C #

    Исключения C # представлены классами. Исключительные классы в C # в основном прямо или косвенно получены из класса System.Exception . Некоторые классы исключений, полученные из класса System.Exception, представляют собой классы System.ApplicationException и System.SystemException .

    Класс System.ApplicationException поддерживает исключения, создаваемые прикладными программами. Следовательно, исключения, определенные программистами, должны вытекать из этого класса.

    Класс System.SystemException является базовым классом для всех предопределенных системных исключений.

    В следующей таблице приведены некоторые предопределенные классы исключений, полученные из класса Sytem.SystemException :

    Обрабатывает ошибки ввода-вывода.

    Обрабатывает ошибки, возникающие, когда метод ссылается на индекс массива вне диапазона.

    Обрабатывает ошибки, возникающие, когда тип несовместим с типом массива.

    Обрабатывает ошибки, возникающие при ссылке на нулевой объект.

    Обрабатывает ошибки, возникающие при делении дивиденда на ноль.

    Обрабатывает ошибки, возникающие при типизации.

    System.OutOfMemoryException

    Обрабатывает ошибки, возникающие из-за недостаточной свободной памяти.

    System.StackOverflowException

    Обрабатывает ошибки, возникающие при переполнении стека.

    Обработка исключений

    C # обеспечивает структурированное решение обработки исключений в виде блоков try и catch . Используя эти блоки, операторы основной программы отделены от операторов обработки ошибок.

    Эти блоки обработки ошибок реализованы с использованием ключевых слов try , catch и finally . Ниже приведен пример исключения исключения при делении на нулевое условие:

    Когда приведенный выше код компилируется и выполняется, он производит следующий результат:

    Создание пользовательских исключений

    Вы также можете определить свое собственное исключение. Пользовательские классы исключений выводятся из класса Exception . Следующий пример демонстрирует это:

    Когда приведенный выше код компилируется и выполняется, он производит следующий результат:

    Глобальная обработка исключений в С#

    В предыдущей статье рассказывалось про глобальную обработку исключений в C++. В этой статье будет показано, как решить ту же задачу в языке C#.


    В .Net есть событие AppDomain.CurrentDomain.UnhandledException которое вызывается для всех не перехваченных исключений. Оно позволяет приложению записать в журнал информацию об исключении до того, как системный обработчик по умолчанию сообщит пользователю об исключении и прекратит выполнение приложения. Если имеется достаточно информации о состоянии приложения, можно выполнить другие действия, такие как сохранение данных программы для последующего восстановления. Рекомендуется действовать осторожно, поскольку данные программы могут быть повреждены, если исключение не обработано.

    Событие Application.ThreadException позволяет приложению Windows Forms обрабатывать исключения, возникающие в потоках Windows Forms, которые в противном случае не были бы обработаны. Присоедините обработчики событий к событию ThreadException, чтобы обработать эти исключения, которые оставят приложение в неопределенном состоянии. Если это возможно, исключения следует обработать с помощью блоков структурной обработки исключений.

    Чтобы добавить свой глобальный обработчик исключений, нужно подписаться на данные события:

    Данные методы не предназначены для штатного использования. Программист должен обрабатывать исключения «на месте» с использованием конструкции try-catch. Данные методы предназначены для получения информации в экстренных ситуациях. Эти ситуации указывают на наличие проблем с коде, которые должны быть устранены.

    Есть статья по решению такой же задачи в C++: Глобальная обработка исключений в С++

    C# — Проблема с программой (C#)

    Всем привет, возникли проблемы с методами в Visual Studio 2010, при написании программы, может как то нужно объявить их в программе? начал писать вторую программу и опять столкнулся с этим методом и тоже самое ругается на него. Подскажите пожалуйста что сделать нужно?

    Ошибка 1 : Наиболее подходящий перегруженный метод для «NewProg.db_DataSet.VidLicDataTable .FindByidVidLic(int)» имеет несколько недопустимых аргументов строка 29

    Ошибка 2 : Аргумент «1»: преобразование типа из «string» в «int» невозможно строка 29

    Предлагает : Создать заглушку метода FindBy >
    Если создать начинает показывать ошибку:
    «object» не содержит определения для «vlName» и не был найден метод расширения «vlName», принимающий тип «object» в качестве первого аргумента (возможно, пропущена директива using или ссылка на сборку)

    Обработка исключений для C#

    View more Tutorials:

    1- Что такое Exception?

    Сначала давайте посмотрим на следующий иллюстративный пример:

    В этом примере есть часть кода c ошибкой, которая получается из-за деления на 0. Деление на 0 вызывает исключение: DivideByZeroException

    Мы будем модифицировать код примера выше:

    2- Иерархия исключений

    Exception

    Основной класс всех исключений.

    SystemException

    Основной класс всех исключений генерированных во время запуска программы.

    IndexOutOfRangeException

    Выбрасывается во время запуска при доступе в элемент массива с неправильным индексом.

    NullReferenceException

    Выбрасывается во время запуска при ссылке на объект null.

    AccessViolationException

    Выбрасывается во время запуска при ссылке к недействительной памяти.

    InvalidOperationException

    Выбрасывается методом при недействительном статусе.

    ArgumentException

    Основной класс всех исключений связанных с аргументом (Argument).

    ArgumentNullException

    Это подкласс ArgumentException, который выбрасывается методом, не позволяющим передачу аргументов null.

    ArgumentOutOfRangeException

    Это подкласс ArgumentException, который выбрасывается методом когда аргумент не соответствует рамке позволяющей передачу.

    ExternalException

    Основной класс для исключений или пришедших из внешней среды.

    COMException

    Класс расширенный из ExternalException, исключение упаковывает информацию COM.

    SEHException

    Класс расширенный из ExternalException, ловил все исключения из Win32.

    C# — Проблема с программой (C#)

    В этой статье опубликованы ответы на часто появляющиеся вопросы при программировании в среде C# от Microsoft.

    См. также: преобразование даты, времени в строку Q014, преобразование чисел в строку Q015, преобразование строк в числа Q022.

    В среде Visual Studio C# удобно делать пакет установки, не нужно разбираться с инсталляторами программы (внешние дополнительные инсталляторы теперь не нужны), которую Вы пишете. Для этого в меню достаточно выбрать в меню Построение (Build) -> Опубликовать (Release). Это так называемая технология ClickOnce [8]. В папке Release сразу появится подпапка app.publish, в которой будет лежать готовый инсталлятор setup.exe, содержащий все необходимые для установки программы компоненты.

    Также см. вопрос Q073.

    Почему такая проблема возникает: ListBox создается в основном потоке (thread), запускающей GUI окна программы. Другие потоки могут быть созданы для поддержки обмена данными с внешними устройствами (сеть Ethernet, COM-порт, USB и т. п.). Эти другие потоки могут обрабатывать события приема данных асинхронно с выполнением основного потока. Поэтому если эти потоки попытаются что-то записать в ListBox, то это может привести к конфликту с записью в ListBox данных из основного потока GUI интерфейса программы.

    Как проблему можно решить: для ListBox создаются подпрограммы (методы), через которые выводятся данные в ListBox. Данные выводятся в ListBox только через эти подпрограммы, больше никак. Эти подпрограммы делаются защищенными (thread-safe) для вызова из любых потоков через delegate, InvokeRequired, Invoke.

    См. также:
    — справку в MSDN: Control.InvokeRequired — свойство, Control.Invoke — метод (Delegate), How to: Make Thread-Safe Calls to Windows Forms Controls
    — Making Windows Forms thread safe

    В C# уже нельзя, как раньше, беспечно задавать константы операторами #define, и добавлять их h-файлы (как было принято в языках C и C++). Для C# нельзя использовать заголовки (файлы с расширением *.h или *.hpp), и директива #define не может использоваться для задания констант.

    Для использования #define теперь такие правила:

    1. Добавлять определения констант #define можно только в начале файла, иначе получите ошибку CS1032.
    2. Директива define может использоваться только для задания ключей компиляции, но не для задания констант, иначе получите ошибку CS1025: Требуется однострочный комментарий или признак конца строки.

    [Правила определения констант]

    Константы в C# задаются как обычные переменные, только с помощью ключевого слова const:

    Однако если Вы попытаетесь сделать это вне пространства имен класса, то получите ошибку CS0116: Пространство имен не может непосредственно содержать такие члены, как поля или методы. Константы нужно определять не только внутри скобок одной из секций namespace, но и в каком-то определенном классе. Например так (определение константы USBCAN_SUCCESSFUL):

    Вместо #define удобно также использовать перечисления (enum). Вот пример задания масок для бит сигналов:

    [Как определять глобальные константы]

    Чтобы определить константы, которые видны в любом классе пространства имен приложения, нужно создать отдельный класс. Этот класс с константами обязательно должен быть размещен после кода класса формы, иначе конструктор формы не запустится — несмотря на то, что приложение компилируется без ошибок. Если попытаться запустить такое скомпилированное приложение, то возникнет ошибка MissingManifestResourceException (см. вопрос Q019).

    Пример правильного определения глобальных констант в файле Form1.cs с помощью глобального статического класса CNST :

    Теперь можно пользоваться константами в любом месте пространства имен namespace MyApp через префикс класса CNST:

    И еще добавьте обработчик события приема сообщения CANsrv_CanMsgReceivedEvent:

    Для упрощенного добавления обработчика выполните следующие шаги:

    — в коде Form1_Load напечатайте CANsrv. (в конце поставьте точку);
    — нажмите Ctrl+Space, из появившегося списка методов и событий выберите CanMsgReceivedEvent;
    — допечатайте пробел, + и =. Появится подсказка в виде текста:
    «new USBcanServer.CanMsgReceivedEventEventHandler(CANsrv_CanMsgReceivedEvent); (Нажмите клавишу TAB для вставки)»
    — нажмите клавишу TAB, и в коде автоматически будет создана строка:

    После этого останется вручную создать процедуру CANsrv_CanMsgReceivedEvent. Полный код см. в [2].

    На C# будет так (только для компиляции в небезопасном режиме, с опцией /unsafe):

    Если код компилируется в безопасном режиме, то на C# структура tCanMsgStruct должна быть определена так:

    Экземпляр структуры определяется так:

    Если не нужно точное соответствие положения полей в памяти, то структуры можно определять обычным способом, через ключевое слово struct. Такие структуры на C# почти что классы, и могут иметь даже собственные конструкторы, в которых можно инициализировать массивы, входящие в структуру как поле. Как и классы, в составе полей такие структуры могут иметь классы, атрибуты и методы. Пример структуры с конструктором:

    Если компилируется небезопасный код (применяется опция /unsafe), то можно определять массивы так:

    [Многомерные массивы]

    Определение массива 2×8 байт:

    public byte [,] buf = new byte [2, 8];

    Форматированный вывод в строку (то, что на C++ делает printf с параметрами форматирования %) в C# можно делать через метод Format, который есть у класса string. Тип форматирования зависит не только от строки формата, но и от типа выводимых параметров.

    Строка формата задается теперь не символом %, а фигурными скобками. Первое число, которое идет после открывающей фигурной скобки, задают номер выводимого в строку параметра. Если после этого числа стоит двоеточие, то за ним идут символы до закрывающей фигурной скобки, которые определяют формат вывода параметра. Для того, чтобы понять, как пользоваться Format, проще рассмотреть несколько простых примеров.

    [Целые десятичные числа]

    [Целые числа в HEX-формат]

    [Числа с плавающей точкой]

    [Вывод строки]

    См. также: преобразование даты, времени в строку Q014, преобразование строк в числа Q022.

    По умолчанию Windows на русском языке число с плавающей точкой выводит в формат с использованием запятой:

    Но как быть, если вместо запятой нужно выводить точку?

    На языке C# значение ключевого слова static поменялось. Это слово можно использовать только для глобальных переменных класса, и оно будет означать следующее — переменная класса, которая объявлена с ключевым словом static, будет иметь общее значение между всеми экземплярами этого класса. Т. е. она будет «глобальной» для всех экземпляров этого класса.


    Подробнее про значение ключевого слова static на C# см. статью [7].

    Меню Проект -> Добавить форму Windows. -> Форма Windows Forms -> OK. По умолчанию будет добавлена форма Form2. Далее в модуле Program.cs добавьте код для отображения второй формы:

    После запуска программы отобразятся сразу две формы, Form1 и Form2. Подробности см. в [4].

    Проблема возникла после попытки добавить глобальный класс констант в пространство имен приложения (в данном примере calibr) перед классом определения формы. Так неправильно, при запуске приложения будет происходить ошибка MissingManifestResourceException, и конструктор формы не будет работать:

    Решение проблемы: нужно перенести класс констант за определение класса формы. Вот так правильно:

    Пример использования функции с переменным числом параметров (запись в лог):

    Ответ: изменять свойство Location нужно через вспомогательную переменную следующим образом:

    Похожим образом нужно менять и размер окна формы, так как прямым присвоением Form.Size.Width и Form.Size.Height этого сделать нельзя (появится ошибка CS1612). Менять размер формы нужно так:

    См. методы класса Convert — ToBoolean, ToByte, ToChar, ToDecimal, ToDouble, ToInt16, ToInt32, ToInt64, ToUInt16, ToUInt32, ToUInt64 и другие.

    [Простой случай преобразования в целое число со знаком]

    [Преобразование строки символов в HEX-формате]

    • Преобразование даты, времени в строку Q014.
    • Преобразование чисел в строку Q015.

    Чтобы при запуске программы в ComboBox отображалось нужное значение из списка Items, программно поменяйте свойство SelectedIndex.

    Подробности по ComboBox см. в [3].

    то попробуйте исправить определение класса MyClass, добавив public:

    Модификатор static можно использовать с классами, полями, методами, свойствами, операторами, событиями и конструкторами, но нельзя — с индексаторами, деструкторами или типами, отличными от классов. На статический член нельзя ссылаться через экземпляр, а можно только через имя типа. Подробности смотрите в MSDN, раздел «static (Справочник по C#)».

    А вот так нужно исправить код, чтобы не возникала ошибка System.StackOverflowException в System.Windows.Forms.dll:

    Второй способ зафиксировать размер формы во время выполнения — задайте обработчик события OnResize, в котором установите свойства Width и Height в нужное значение.

    Этим же способом можно узнать и высоту текста в пикселах (для нашего примера это будет stringSize.Height).

    Используйте метод ShowDialog(), подробности см. в [4].

    См. также вопрос «Как запустить внешнее приложение?».

    Однако можно создать экземпляр класса (компонент) во время выполнения программы, что решит проблему. На примере таймера Timer с Панели Элементов, который добавляется программно в невизуальный класс MyClass:

    Методы

    Пояснения Имя исполняемого файла в форме myApp.vhost.exe Каталог, где находится исполняемый файл (полный путь каталога). Получим строку типа «myApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null» Полный путь до исполняемого файла в виде c:\VisualStudioProjects\myApp\bin\Debug\myApp.exe

    Получение имени исполняемого файла без пути и расширения, получим что-то типа строки «myApp».

    Ошибка 17 Не удалось опубликовать из-за того, что не удалось построить проект. 1
    Ошибка 18 При подписи произошла ошибка: Не удалось подписать bin\Release\app.publish\\setup.exe. SignTool Error: The signer’s certificate is not valid for signing.
    SignTool Error: An error occurred while attempting to sign: bin\Release\app.publish\\setup.exe C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets 3987

    Решение проблемы: зайдите в меню Проект -> Свойства проекта, снимите галочку «Подписать манифесты ClickOnce». После запуска публикации система выдаст запрос: «Приложение подписано не тем ключом, что существующее на сервере приложение. Перезаписать его?», ответьте утвердительно.

    К сожалению, C# не поддерживает объединения C/C++. Однако с той же целью можно использовать StructLayout(LayoutKind.Explicit) и атрибуты FieldOffset. Предположим, что у Вас есть структура из 64 бит данных, и Вам нужно представить её либо как 8 байт (byte), либо как два целых 32-разрядных числа (int). Атрибут FieldOffset регулирует смещение нужных нам частей данных. Например:

    [Пример1: проблема с ulong и int]

    При попытке задать в перечислении 64-битную величину получаю ошибку CS0266 «Не удается неявно преобразовать тип «ulong» в «int». Существует явное преобразование (возможно, пропущено приведение типов) C:\csharpprj\protocol.cs . «.

    Решение проблемы: по умолчанию перечисление (enum) на C# подразумевает использование целых чисел (int). Чтобы исправить ошибку, добавьте явное указание типа для перечисления (можно указать типы byte, sbyte, short, ushort, int, uint, long или ulong):

    [Пример2: проблема с типом byte]

    Вот такой простой код неожиданно дает ошибку CS0266:

    Проблема тут в том, что оператор + в дает в результате тип int. Решение проблемы:

    В С++ он вызывается напрямую механизмами языка. Это происходит так же быстро, как и вызов метода из кода пользователя (ну, пожалуй, совсем ненамного дольше).

    В С# деструкторы вызываются не пользовательским кодом, а средой CLR. Код C# потому и называется управляемым, что по сути он запускается под управлением среды. Поэтому, к примеру, здесь нет указателей — среда сама выполняет операции с указателями автоматически и не поддерживает код, который их использует. Механизм использования указателей все же оставлен в целях совместимости, но при этом классы, их использующие, надо помечать ключевым словом unsafe, разрешать выполнение этого unsafe кода в свойствах проекта. Но самое главное — среда не будет управлять этим кодом — она передаст ему управление и будет дожидаться, когда получит его обратно.

    То же самое относится и к деструкторам. Деструкторы призваны освободить ресурсы, занимаемые классом. Но класс не может освободить ресурсы, которыми фактически не владеет. А по факту владеет он только теми кусками, которыми управляет сам. Поэтому деструктор вызывается не пользовательским кодом, а из CLR, то есть снаружи. Но не сразу, а только после того, как среда отметит, что на экземпляр класса больше не ссылается ни одной переменной. Отсюда возникает задержка — только после второго прохода сборщика мусора. И отсюда потеря производительности — мы заставляем сборщик мусора отслеживать еще и деструкторы.

    Моя практика показала, что при уничтожении около полусотни объектов с деструкторами (причем до крайности простыми — из статической переменной вычиталась единица) комп «подвисает» так, что это становится заметно пользователю. Замеры показали диапазоны 18 .. 47 мс, т. е. почти всегда «терялся» один кадр на экране, а в некоторых случаях — до двух/трех (при частоте смены кадров в 30 Гц).

    Если при попытке компиляции выводится ошибка о том, что System.Windows.Forms не найдено в пространстве имен (‘Forms’ does not exist in the namespace system.windows), то добавьте на него ссылку в проекте. Для этого сделайте правый клик на дерево файлов проекта (Solution Tree), выберите в контекстном меню Добавить ссылку. в появившемся окне «Добавить ссылку» перейдите на закладку .NET, прокрутите список и выберите в нем System.Windows.Forms, нажмите OK.

    Еще один пример подпрограммы вывода модального диалогового окна с выходом из приложения, если установлен в true параметр exit:

    Как исправить: зайдите в свойства проекта (Project -> Properties. ), перейдите в раздел Подписывание (Signing), нажмите кнопку «Создать тестовый сертификат. » (create test certificate). Появится окно запроса пароля сертификата. Оставьте поля пустыми и нажмите OK, после чего опубликуйте проект.

    1. Откройте Панель элементов, выберите PictureBox, бросьте на форму.
    2. Выберите свойства pictureBox1 -> BackgroundImage, нажмите справа кнопку с многоточием, откроется окно выбора ресурса. Выберите уже имеющийся ресурс, или нажмите внизу кнопку Импорт. и выберите нужную картинку (формата BMP или PNG).
    3. По умолчанию свойство BackgroungImageLayout установлено в Tile, что означает, что картинка будет размножена мозаикой по всей площади pictureBox1 (границы этой площади можно раздвинуть мышкой). Чтобы картинка была только одна, выберите значение None для свойства BackgroungImageLayout. Также свойство SizeMode установите в AutoSize, если хотите, чтобы размеры pictureBox1 автоматически подстраивались под размер картинки.
    4. Чтобы поменять картинку runtime (во время выполнения программы), нужно присвоить свойство BackgroundImage, пример:

    Здесь door_closed и door_disabled — ссылки на импортированные картинки door_closed.png и door_open.png.

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

    Как нарисовать черную линию:

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

    В VB такая операция происходит присвоением object=nothing, на C++ применяется оператор delete. Как явно удалять объекты на C#?

    В C# можно сделать аналогичное действие object=null, но в действительности присвоение object=null не удаляет объект, а просто делает недействительной ссылку на него. В .NET это дает сигнал сборщику мусора (garbage collector) удалить объект из памяти. Но Вы не можете знать, когда конкретно это произойдет.

    В C# при создании объекта оператором new память для него выделяется из кучи (heap). Вы также можете Объект удалится автоматически тем же сборщиком мусора, когда управление выйдет за пределы существование объекта (например, когда завершается функция, которая создала объект оператором new). Но опять-таки неизвестно, в какой момент произойдет освобождение памяти, связанное с удалением объекта.

    Если в C# у объекта есть метод Dispose(), то его нужно обязательно вызвать до того, как управление выйдет из сферы действия объекта. Вы можете использовать ключевое слово using, чтобы сделать эту операцию автоматически, например:

    Принудительно запустить процедуру сбора мусора можно вызовом GC.Collect(), но это делать не рекомендуется.

    Для произвольного класса:

    Для метки WinForm:

    Если у Вас есть экземпляр x класса A внутри namespace B, то можете использовать:

    В строке s не будет содержаться «B». Вы можете также использовать x.GetType().Name, чтобы получить имя типа, или x.GetType().FullName чтобы получить полное имя, вместе с B.

    Другой вариант, поместите в Вашу сборку функцию:

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

    [Вариант 1]

    Чтобы в Windows Forms нарисовать метку с вертикальным текстом, нужно переопределить событие Paint, и рисовать в нем метку вертикально. Имейте в виду, что GDI оптимизировано для горизонтального рисования меток. Если Вы повернете текст (даже если угол поворота будет кратен 90 градусов), то возможно это будет работать не очень качественно.

    Возможно лучшим решением будет нарисовать текст (или поручить метке нарисовать себя) в растровую картинку (bitmap), и затем отобразить эту картинку повернутой.

    Ниже приведен код, который рисует Custom Control с вертикальным текстом. Имейте в виду, что текст ClearType не работает, если текст не горизонтальный.

    Можно повернуть текст в событии OnPaint, или в методе Paint:

    [Вариант 2]

    Создайте свой класс myLabel, который может поворачивать текст. Этот класс можно будет перетаскивать в форму приложения прямо из панели компонентов ToolBox.

    Для этого класса можно установить свойства:

    Улучшенная реализация, поддерживающая автоматическое изменение размеров виджета:

    Отличный, рабочий пример есть в MSDN (ключевая строка для поиска ICustomFormatter.Format Method site:msdn.microsoft.com ).

    См. также: преобразование даты, времени в строку Q014, преобразование чисел в строку Q015, преобразование строк в числа Q022.

    ‘Необработанное исключение типа «System.BadImageFormatException» произошло в имя_приложения.exe

    Дополнительные сведения: Была сделана попытка загрузить программу, имеющую неверный формат. (Исключение из HRESULT: 0xXXXXXXXX)’

    Чаще всего проблема из-за того, что вызов функции (метода) потребовал вызова DLL, скомпилированной под другую систему. Например, программа работает на 64-битной версии Windows, а DLL сделана для 32-битной версии. Что можно сделать:

    1. Найти DLL, которая скомпилирована под Вашу операционную систему.

    2. Если у Вас 64-битная система, а загружаемая DLL 32-битная, то можно изменить в свойствах тип конечной платформы на 32-битную. Для этого откройте свойства проекта, перейдите в раздел Построение, и выберите в выпадающем списке «Конечная платформа:» вариант x86 (см. скриншот ниже). После этого пересоберите проект.

    Для этого достаточно поменять свойство формы Text. Пример:

    [Текст модуля myform.cs]

    Можно также к свойству Text обращаться через объект this:

    Для этого создайте событие FormClosing, и в его обработчике присвойте FormClosingEventArgs.Cancel значение true. Пример:


    После этого на форме будет отображаться стандартная кнопка с крестиком, но она перестанет действовать. Подробнее про поведение события закрытия формы см. [6].

    Можно ли в C# декларировать переменную таким образом, чтобы в каждом классе (представленном модулем *.cs) можно было получить доступ к содержимому этой переменной без необходимости создавать экземпляр какого-либо класса? Другими словами, если ли в C# глобальные переменные, не привязанные к какому-либо экземпляру класса? Есть ли какая-то альтернатива обмениваться данными между классами через одну внешнюю переменную?

    В C# не существует такого понятия, как глобальная переменная, и точка. Однако есть некий аналог в виде модификатора public static , который можно применять к классам и к отдельным членам класса. Короче говоря, используйте класс типа public static, и получайте доступ к нему где угодно и откуда угодно.

    Можно использовать переменные этого класса в любом месте, когда они находятся в одном и том же пространстве имен (namespace), что и ссылающийся на переменные код. Можно объявить MyStaticValues или Globals без пространства имен (что поместит эти статические классы в глобальное пространство имен приложения, application namespace), или Вы работаете с разными пространствами имен, то можете указать нужное пространство следующей директивой:

    [Как использовать аналоги глобальных переменных в C#]

    Сначала хорошенько подумайте — действительно ли Вам нужна глобальная переменная. Скорее всего решение должно быть принято, исходя из рассмотрения техзадания на разрабатываемое программное обеспечение. Обычно статические переменные могут пригодиться для таких объектов в программе, которые имеются исключительно в одном экземпляре, их не нужно создавать и удалять динамически, и если эти объекты подразумеваются для использования несколькими разными классами в программе. Имейте в виду, что иногда проблемы, возникающие из-за конкуренции доступа к глобальным переменным (особенно актуально в сложных системах), бывает трудно отладить, поэтому будьте готовы к трудностям.

    Итак, если положительное решение принято и глобальные переменные все-таки нужны, то действуйте примерно по такой схеме:

    1. Поместите все глобальные переменные в один static класс (для удобства управляемости кодом).
    2. Используйте внутри этого класса обертки get/set для доступа к «глобальным» переменным. Это предоставит дополнительный механизм для отладки проблем, возникающих из-за конкурентного доступа.

    Вот базовый пример для такого класса:

    Использование переменных этого класса из других классов (находящихся в том же пространстве имен namespace) может быть таким:

    Хорошая информация по теме static классов C# есть в статье «Статика в C#», опубликованной на Хабрахабр (ключевые слова для поиска Статика в C# site:habrahabr.ru).

    Пример определения дочернего класса TMyClass внутри родительского класса myform:

    После этого можно обращаться к экземплярам классов TMyClass по индексу (вызывать его методы, обращаться к свойствам). Если не сделать инициализацию элементов массива, то при запуске программы и попытке доступа к экземплярам классов из массива получите ошибку «ссылка на объект не указывает на экземпляр объекта».

    У меня была следующая проблема: KeyDown срабатывает нормально, пока не добавлена на форму кнопка. Как только кнопка добавлена, KeyDown перестает работать.

    [Решение 1]

    Нужно установить у формы свойство KeyPreview=true. Этот совет можно найти на большинстве форумов, однако он помогает не всегда — возможно, это зависит от версии библиотек Visual Studio C#. Мне не помогло (Microsoft Visual Studio 2010 Версия 10.0.40219.1 SP1Rel).

    [Решение 2]

    Нужно переопределить метод ProcessCmdKey формы. Для этого просто добавьте в класс формы следующий код:

    Обратите внимание, что при возврате true из метода ProcessCmdKey не будет отправлено событие нажатия на элемент управления, на котором установлен фокус.

    Окно имеет высоту (свойство Height ) и ширину (свойство Width ), которые определяют размеры окна формы. Эти свойства можно читать или менять программно. Однако есть еще и «клиентская» область формы, которая меньше. По ширине она меньше на ширину бордюров окна, а по высоте она меньше еще и на размер верхней плашки (title bar).

    [Свойство ClientSize]

    У свойства ClientSize формы есть поля только для чтения — Width и Height, по значению которых можно определить размеры клиентской области формы (ширину и высоту соответственно).

    [Метод SetClientSizeCore]

    Метод SetClientSizeCore принимает 2 параметра — x и y, и позволяет установить размеры клиентской области формы.

    Пример, в котором возникает ошибка (Невозможно получить доступ к нестатическому члену внешнего типа . ):

    [Варианты решения проблемы]

    1. Можно применить к переменной или методу, к которому нельзя получить доступ, атрибут static:

    2. Вместо вложенных классов (nesting) применить наследование (inheritance). Родительский класс должен иметь тип доступа public, чтобы можно было применить наследование:

    3. Создать экземпляр класса, который содержит нужную переменную или метод:

    При попытке обновить содержимое полосы статуса приложения Windows Forms ничего не происходит. Вот пример кода, который по замыслу должен показывать текст, но на самом деле текст не отображается:

    [Решение проблемы]

    Свойство Text класс StatusStrip получает при наследовании из класса ToolStrip (который в свою очередь наследует его из класса Control). Но у свойства Text класса ToolStrip нет визуального эффекта, просто так почему-то спроектирован класс ToolStrip. Поэтому присваивать значение свойству Text класса StatusStrip бесполезно. Поначалу это может запутать.

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

    По умолчанию будет создан элемент класса toolStripStatusLabel1, доступный напрямую из кода формы. Для отображения текста нужно менять его свойство Text, при этом метод Refresh для StatusStrip вызывать не обязательно. Пример:

    Решение взято отсюда: Reading/writing an INI file site:stackoverflow.com.

    Создайте класс IniFile.cs и добавьте его в проект:

    [Открытие ini-файла]

    Открыть ini-файл можно тремя способами:

    В результате Вы получите объект IniFile, после чего можете воспользоваться его методами Read , Write , KeyExists , DeleteKey , DeleteSection .

    [Запись парамеров в ini-файл]

    Предположим, нам нужно получить INI-файл такого вида:

    Для этого можно использовать следующий код:

    Если не указывать секцию (последний параметр), то будет автоматически создана секция INI-файла с именем namespace приложения (в данном примере namespace MyApp).

    [Чтение параметров из ini-файла]

    Пример чтения параметра MyPath из секции SECTION:

    Если не указывать имя секции (последний параметр), то в качестве имени секции будет использоваться имя namespace приложения (в данном примере namespace MyApp).

    [Проверка существования параметра]

    Если параметр MyPath не существует в секции SECTION, то он будет создан и записан:

    [Удаление параметра]

    [Удаление секции]

    Пусть у нас есть вот такой файл, таблица частот (test.xml):

    Ниже показан пример загрузки этого файла в таблицу dataGr >using System.Xml .

    Важное замечание: у метода Load (filename) объекта XmlDocument есть особенность поведения под Windows XP, если в качестве filename передавать не полный, а относительный путь. По крайней мере этот глюк у меня наблюдался на Visual Studio 2010 (.NET Framework версии 4.6.01590 SP1Rel), возможно в последующих версиях это исправлено. Проблема заключается в следующем: в Windows XP первая передача в Load относительного имени файла происходит успешно, но этот каталог запоминается. Все последующие вызовы Load с передачей относительного имени заканчиваются неудачей, потому что к файл ищется по пути, полученному сложением текущего каталога с переданным относительным путем. Под Windows 7 такой проблемы нет, относительные пути всегда отрабатывают без проблем.

    Для обхода этой ошибки приходится в метод Load передавать полное имя файла, тогда все нормально работает и под Windows XP, и под Windows 7:

    ToolStripComboBox это выпадающий список выбора, который можно встроить как элемент меню.

    [Как динамически добавлять элементы списка]

    [Как запретить редактирование элементов списка]

    [Текущий элемент списка]

    Текущий элемент списка ToolStripComboBox доступен на чтение и запись через свойство SelectedIndex. Нумерация элементов начинается с нуля.

    Как проверять выбранный пользователем элемент списка:

    Как устанавливать текущий элемент программно:

    1. Добавьте файл, который нужно устанавливать вместе с приложением, в дерево файлов проекта (Обозреватель решений). Для этого можно просто перетащить файл в корневую папку проекта, отображаемую в Обозревателе решений.

    2. Измените свойство файла «Действие при построении» в значение «Содержание» (по умолчанию там указано «Нет»).

    3. Откройте свойства проекта, зайдите в раздел «Публикация». Кликните на кнопку «Файлы приложения. «. Убедитесь, что нужный файл был добавлен в список таблицы публикуемых файлов. В строке таблицы этого файла должны быть установлены следующие значения:

    Состояние публикации: Включить (Авто)
    Группа загрузки: (Требуется)
    Хеш: Включить

    Так же настраивается публикации папки вместе с содержащимися в ней файлами. Перетащите публикуемую папку в Обозреватель решений и повторите шаги 2 и 3. На шаге 3 следует проверить, что в таблице публикуемых файлов находятся все файлы, содержащиеся в публикуемой папке.

    После попытки запуска инсталлятора (setup.exe) приложение не устанавливается, в логе установки выводится сообщение:

    1. Удалите папку 2.0, которая находится в профиле пользователя. На Windows 7 и Windows Vista эта папка находится в каталоге C:\Users\имя_пользователя\AppData\Local\Apps\. На Windows XP эта папка находится в каталоге c:\Documents & Settings\имя_пользователя\LocalSettings\Apps\. При удалении папки могут потребоваться права администратора. Если удалить папку не получается, то перезагрузите компьютер, закройте все приложения и с правами администратора повторите попытку удаления папки 2.0.

    2. Если после удаления папки 2.0 проблема не исчезла, то запустите с правами администратора команду:

    На примере библиотек .NET Ivi.Visa и NationalInstruments.Visa:

    «Не удалось найти имя типа или пространства имен «Ivi» (пропущена директива using или ссылка на сборку?)»
    «Не удалось найти имя типа или пространства имен «NationalInstruments» (пропущена директива using или ссылка на сборку?)»

    Разверните в Обозревателе решений папки проекта, выполните правый клик на папке «Ссылки», выберите «Добавить ссылку. «. Откроется окно диалога выбора библиотек. На закладке .NET найдите нужные библиотеки (в нашем примере это Ivi.Visa Assembly и National Instruments VISA, можно выбирать несколько библиотек сразу, удерживая клавишу Ctrl) и кликните OK.

    В папке «Ссылки» появятся новые библиотеки, и ошибка «Не удалось найти . » исчезнет.

    Откройте свойства проекта, перейдите в раздел Отладка -> Параметры запуска -> в окне «Аргументы командной строки:» укажите нужные аргументы.

    Необходимо было в момент запуска программы заполнить выпадающий список ComboBox нужными значениями (имена COM-портов) и потом выбрать из этого списка нужный, чтобы он по умолчанию отображался после полной загрузки программы. Все это я делал в обработчике события загрузки формы Form1_Load. Однако в при попытке присвоить значение свойству SelectedIndex срабатывало исключение типа «System.NullReferenceException»: Ссылка на объект не указывает на экземпляр объекта (Object reference not set to an instance of an object).

    Причина ошибки в том, что в момент загрузки формы визуальные элементы еще не полностью инициализированы. Решение проблемы: устанавливать SelectedIndex с помощью вызова события SelectedIndexChanged:

    В состав Visual Studio 2010 входит простой, но иногда довольно неудобный способ для создания дистрибутива (публикации) приложения C#. Это так называемый инструментарий ClickOnce (доступен из меню Построение -> Опубликовать). Такой способ создания дистрибутива всем хорош, но его основное неудобство заключается в том, что невозможно никаким способом задать место установки приложения на компьютере пользователя. Приложение установится автоматически в папку, находящуюся в пределах профиля пользователя (так называемый кэш приложения, Application Cache). Путь до каталога установки приложения получается очень длинным и сложным, на Windows 7 и Windows 10 это будет что-то наподобие C:\ Users\ имя_пользователя\ AppData\ Local\ Apps\ 2.0\ 9HOERDLL.1LY\ KOHZZR9V.QHE\ cali..tion_e314c8db98ca16b5_0001.0000_1b1f3d3fa0feae4c\, или на старых компьютерах с Windows XP это папка c:\ Document and Settings\ имя_пользователя\..

    Причина такого решения в том, что решаются многие проблемы безопасности — для установки и работы приложения не требуется наличие прав администратора. Т. е. пользователь с пониженными правами сам может установить, удалить или обновить приложение, вмешательство администратора не требуется. Пользователь с пониженными правами и приложения, работающие от его имени (в том числе трояны и вирусы), никак не могут нарушить содержимое таких системных папок, как %SystemRoot%, %ProgramFiles%, %ProgramFiles(x86)%, %ProgramData% и т. п.

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

    Недостатки ClickOnce следующие:

    • При установке очень сложный путь до места установки приложения.
    • При обновлении приложения его место расположения на диске постоянно меняется.
    • Несколько копий одного и того же приложения на диске, если его установили несколько разных пользователей компьютера.

    Таким образом, если Вас не устраивает установка ClickOnce, то нет никакого другого выхода, как инсталляция путем ручного копирования скомпилированного приложения в нужную папку, или создание дистрибутива другим способом, например через создание пакета *.msi (Microsoft Windows Installer).

    Преимущества и недостатки C# — Учим Шарп #1

    Опубликовано shwan в 06.11.2020

    Я хочу обсудить с тобой один очень важный вопрос, который достаточно часто упускают из внимания: действительно ли тебе нужно учить язык C#?

    Что бы тебе ни рассказывали на всевозможных онлайн курсах, книгах, тренингах и конференциях, о том, как можно стать программистом за 21 день – все это ложь, п*дежь и провокация.

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

    Поэтому, я предлагаю тебе предварительно рассмотреть основные преимущества и недостатки языка C#, перспективы развития, сферы применения и уровни зарплатных ожиданий. Это может помочь тебе понять действительно ли ты правильно инвестируешь свое время. А время – это самый ценный ресурс, запомни это.

    История языка C#

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

    Данный язык создан всеми так горячо любимой корпорацией Зла Microsoft в 2000 году. Он очень многое унаследовал от своих родителей (С++ и Java), но и привнес нового.

    Возможной версией использования символа хештега принято считать наследование плюсиков от предков, примерно следующим образом: C → C++ → C++++(C#), потому что символ «#» при хорошем воображении можно получить путем объединения 4-х знаков «+».

    Язык активно развивается. Регулярно выходят новые версии C#, которые добавляют новые синтаксические конструкции в язык, а также увеличивают его быстродействие и надежность.

    Преимущества C#

    Данный язык использует объектно-ориентированный подход к программированию во всем. Это означает, что тебе нужно будет описывать абстрактные конструкции на основе предметной области, а потом реализовывать между ними взаимодействие. Данный подход пользуется большой популярностью, потому что позволяет не держать в голове всю информацию, а работать по принципу черного ящика: подал входные данные -> МАГИЯ -> PROFIT.

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

    Еще стоит упомянуть, что все это работает на базе платформы .NET Framework. Что это означает? Для многих непосвященных, это просто какая-то приблуда, которую нужно установить на комп, чтобы программа запустилась, но дело обстоит значительно глубже. Написанный тобой код на языке C# транслируется в промежуточный язык (IL), который в свою очередь уже преобразуется в машинный код на твоем компьютере прямо во время выполнения приложения (JIT). Спрашивается, зачем это все? А суть в том, что ты можешь пилить со своим другом Васей на разных языках один и тот же проект и ни одному из вас не придется переучиваться. Но я никогда не видел, чтобы это реально использовали на практике. Но это еще не все. Так как окончательная компиляция из промежуточного кода выполняется в живую на твоей конкретной машине, то возможно увеличение производительности за счет использования специфических команд именно твоего процессора.

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

    Ты просто скачиваешь нужное тебе решение из nuget и начинаешь его использовать. В большинстве своем они бесплатны. Сюда же можно отнести большое количество обучающего и справочного материала. Практически на любой свой вопрос ты сможешь найти ответ на стековерфлоу. Ну а на крайний случай всегда можешь спросить в моем телеграм чате для программистов.

    Немаловажно наличие хороших инструментов разработки, и здесь все очень хорошо. Абсолютное большинство разработчиков используют интегрированную среду разработки Visual Studio, которая предоставляет over9000 возможностей, использовать которые ты конечно же не будешь.

    Но IDE действительно хороша, к тому же имеется ее полнофункциональная бесплатная версия Community.

    Еще к плюсам можно отнести строгую типизацию, которая позволяет защититься от дурака, и не так давно появившаяся кросспратформенность в .NET Core (да-да, мелкомягкие потихоньку захватывают линукс).

    Недостатки C#

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

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

    .NET использует концепцию JIT-компиляции. Это означает, что программа будет скомпилирована в машинные коды по мере необходимости прямо во время работы приложения. С одной стороны, это конечно прикольно, но при первом запуске возможны весьма серьезные тормоза.

    C# не является повсеместно распространенным языком. Большинство программистов сосредоточены в коммерческой Enterprise сфере, что накладывает весьма серьезные ограничения на поиск работы в небольших городах, где кроме Delphi или PHP ничего жизни не видели. К тому же, как бы то ни было, C# в первую очередь ассоциируется с Windows. Вряд ли в обозримом будущем что-то изменится и Винда все также будет продолжать доминировать на рынке, но все же небольшой риск остается, особенно учитывая недавние фейлы с обновлениями в Windows 10.

    Перспективы развития C#

    C# входит в первую пятерку самых популярных языков программирования на 2020 год. Он используется во многих крупных компаниях, а также и в небольших стартапах. Сейчас компания Microsoft делает большой упор на развитие универсальности и кросспалтформенности для этого языка. Уже сейчас с его помощью можно разрабатывать практически любой тип приложений.

    Компания Microsoft остается одной из крупнейших IT компаний мира, а C# ее флагманский язык программирования, который постоянно развивается и впитывает в себя все новые возможности. Поэтому в обозримом будущем проблем у данного языка возникнуть не должно.

    Сферы применения языка C#

    В этой области C#, наверное, впереди планеты всей. Хочешь разрабатывать обычные приложения для компьютера – пожалуйста, стандартные WinForms Application и консоль тебе в помощь. Хочешь такие же, но покрасивее? – используй WPF. И специальные приложения для магазина в Windows Store тоже. Веб-приложения? – Легко ASP.NET всегда придет на помощь. На Linux? – тоже не вопрос, .NET Core уже здесь. Мобильное приложение? – Xamarin сделает сразу под все платформы. Хочешь написать игру? – движок Unity показывает себя очень даже неплохо, и при этом также адаптирует игру под различные платформы. Хочешь приблизить апокалипсис с восстанием машин и создаешь искусственный интеллект? – есть целая платформа с кучей инструментов для этого Microsoft AI Platform. Также и для компьютерного зрения и ботов. Я вообще с трудом могу придумать пример того, что невозможно реализовать на C#. Я где-то встречал даже операционную систему написанную на шарпе. Поэтому в этой области все хорошо.

    Зарплаты разработчиков C#

    Здесь все в целом неплохо. По данным на 2020 год, C# явно не является самым высокооплачиваемым языком, но и не самый низкооплачиваемый тоже. Среднее значение зарплаты для данного языка в России около 90к рублей. Это весьма неплохой результат, но бесспорно есть и более дорогие языки. В целом, уровень заработной платы намного больше зависит от прямоты рук и уровня знаний, поэтому не так важно на каком языке писать, главное делать это хорошо. Но данная цифра задает примерный уровень для Middle разработчика в среднем по России. От этого уже можно отталкиваться.

    Итоги

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

    В целом это перспективный язык, достаточно простой для освоения начинающим. Но следует помнить, что существует множество альтернатив, которые лучше подходят под некоторые задачи. Например, могу порекомендовать языки C++, Java, Python.

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

    Основная идея C# — это универсальность. Ты можешь реализовать все на одном языке и для любой платформы, но с большой долей вероятности это будет работать медленнее, чем разработанное специально под конкретную платформу.

    Он неплохо подходит как для начинающих в качестве первого языка, так и для реализации крупных коммерческих проектов.

    На этом мы заканчиваем наш первый урок. Подписывайтесь на мои социальные сети: Вконтакте, Телеграм, YouTube и Дзен. Ну а еще есть специальный закрытый чат, для изучающих C# по моему курсу. С вами был Вадим. Пока!

  • Понравилась статья? Поделиться с друзьями:
    Все языки программирования для начинающих