Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
2279 / 1596 / 399
Регистрация: 26.06.2017
Сообщений: 4,719
Записей в блоге: 1

Повсеместная обработка исключений в UI

26.06.2025, 09:54. Показов 2572. Ответов 21
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Добрый день.

Хотелось бы услышать как кто решает такую задачу:
допустим есть некое приложение, которое использует БД или какой-нибудь сетевой ресурс через классы посредники. Получается так, что каждый вызов методов этих посредников надо оборачивать в try чтобы приложение не рухнуло, например при потере сетевого соединения и т.п. Но приложение может быть очень развитым и эти try раздувают код до безобразия. Как такие задачи решаются? Чтобы и исключения ловить и код не раздувать.
0
Лучшие ответы (1)
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
26.06.2025, 09:54
Ответы с готовыми решениями:

Обработка исключений. Как организовать общий обработчик исключений?
У меня есть последовательность вызова методов для внесения данных в таблицу БД. Сам метод...

Обработка исключений
В моей программе при обработке исключения вызывается метод out_of_range ...

WCF+обработка исключений. Как это работает?
Привет. Не могу получить детали исключения на клиентской стороне. Сервис возбуждает исключение...

21
Эксперт .NET
 Аватар для Usaga
14078 / 9295 / 1347
Регистрация: 21.01.2016
Сообщений: 34,902
26.06.2025, 10:01
Uswer, так глобальные обработчики исключений же:

C#
1
2
3
4
5
6
7
8
9
10
11
12
public static void Main()
{
    AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyHandler);
 
    // Дальнейшая инициализация
}
 
static void MyHandler(object sender, UnhandledExceptionEventArgs args) 
{
    var e = (Exception) args.ExceptionObject;
    // Логируем и MessageBox.Show вызываем
}
0
Эксперт JavaЭксперт по электроникеЭксперт .NET
 Аватар для wizard41
3383 / 2698 / 573
Регистрация: 04.09.2018
Сообщений: 8,506
Записей в блоге: 3
26.06.2025, 10:27
Uswer,
ExceptionHandler Class
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
    public static class ExceptionHandler
    {
        public static void Init()
        {
            Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
            Application.ThreadException -= Handle;
            Application.ThreadException += Handle;
            AppDomain.CurrentDomain.UnhandledException -= CurrentDomain_UnhandledException;
            AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
        }
 
        static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            //здесь обрабатываются исключения не UI потоков
            if (e.ExceptionObject is Exception ex)
            {
                while (ex.InnerException != null)
                    ex = ex.InnerException;
            }
            MessageBox.Show(e.ExceptionObject.ToString());
        }
 
        static void Handle(object sender, ThreadExceptionEventArgs e)
        {
            var ex = e.Exception;
            while (ex.InnerException != null)
                ex = ex.InnerException;
 
#if DEBUG
            using (var exceptionDlg = new ThreadExceptionDialog(ex))
            {
                var res = exceptionDlg.ShowDialog();
                if (res == DialogResult.Abort)
                    Application.Exit();
            }
#else
            MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
#endif
        }
    }

в Program:
C#
1
2
3
4
5
6
7
8
        static void Main()
        {
            ExceptionHandler.Init();
 
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
0
HF
 Аватар для HF
1303 / 882 / 199
Регистрация: 09.09.2011
Сообщений: 2,590
Записей в блоге: 2
26.06.2025, 10:28
Цитата Сообщение от Uswer Посмотреть сообщение
надо оборачивать в try чтобы приложение не рухнуло
Чем сложнее приложение, тем меньше возможности иметь универсальный обработчик.
То что Usaga написал - это уже "последний выдох" приложения. Шанс залогировать перед закрытием.
Но обработчики на то и нужны, чтобы отловить и принять решение - сформировать внятное сообщение, проигнорировать, внести другие изменения, закрыться и т.п. Так что или ты контролируешь процесс работы или код у тебя не раздутый.
4
Эксперт .NET
 Аватар для Wolfdp
3785 / 1762 / 371
Регистрация: 15.06.2012
Сообщений: 6,539
Записей в блоге: 3
27.06.2025, 12:19
Лучший ответ Сообщение было отмечено Uswer как решение

Решение

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

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

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public abstract class SafeCommand : ICommand
{
    public event EventHandler? CanExecuteChanged;
 
    public virtual bool CanExecute(object? parameter)
        => true;
 
    public void Execute(object? parameter)
    {
        try
        {
            TryExecute();
        }
        catch (Exception ex)
        {
            EcxeptionHandler(ex);
        }
    }
 
    protected abstract void TryExecute();
 
    protected virtual void EcxeptionHandler(Exception ex)
        => MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
Надеюсь понимаете что это очень упрощённый пример. Более упоротый независимый подход уже подразумевает что Command вообще имеют тонкую логику, которая обращается к BL, в вот в нём уже происходит такая магия

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class NyaCommand(NekoService nekoService) : ICommand
{
    public event EventHandler? CanExecuteChanged;
 
    public bool CanExecute(object? parameter)
        => true;
 
    public void Execute(object? parameter)
        => nekoService.Nya();
}
 
public class NekoService(SafeActionStrategy safeActionStrategy)
{
    public void Nya()
        => safeActionStrategy.Excecute(() => throw new Exception("Nya!"));
}
 
public class SafeActionStrategy(NotificationService notificationService)
{
    public void Excecute(Action action)
    {
        try
        {
            action();
        }
        catch (Exception ex) 
        {
            ExceptionHandler(ex);
        }
    }
 
    public void ExceptionHandler(Exception ex)
        => notificationService.ShowError(ex.Message);
}
 
public class NotificationService
{
    public void ShowError(string message)
    {
        MessageBox.Show(message);
    }
}
Писанины больше, но в случае глобальных правок не приходится перелопачивать каждую команду. Замечу что этот подход частично содран с бэкенда, так что думаю колеги могут запросто опротестовать данный финт ушами.

Цитата Сообщение от HF Посмотреть сообщение
Чем сложнее приложение, тем меньше возможности иметь универсальный обработчик.
То что Usaga написал - это уже "последний выдох" приложения. Шанс залогировать перед закрытием.
+1

Может я не прав, но кажись UnhandledException -- это только для случаев когда нет времени делать нормально.
1
 Аватар для qwerty.123
19 / 18 / 1
Регистрация: 25.05.2025
Сообщений: 39
27.06.2025, 12:46
Цитата Сообщение от Wolfdp Посмотреть сообщение
Может я не прав, но кажись UnhandledException -- это только для случаев когда нет времени делать нормально.
Нет. Это место, предназначенное для отлова неожиданных исключений, не отловленных нигде выше в коде. Предназначено для того, чтобы как следует залогировать ошибку и затем сразу завершить работу приложения.

UPD. При этом полагаться на это событие на 100% не стоит. Насколько я помню, оно реагирует на необработанное исключение в контексте синхронизации (т.е. мониторит конкретный поток выполнения). А если ошибка будет в другом потоке, в т.ч. если в контексте синхронизации вы дополнительно запустите код в новом потоке, в котором и произойдёт исключение, то приложение просто аварийно завершит работу, насколько я помню. Чтобы этого избежать, код любого потока нужно всегда оборачивать в свой отдельный try/catch.
0
2279 / 1596 / 399
Регистрация: 26.06.2017
Сообщений: 4,719
Записей в блоге: 1
27.06.2025, 21:52  [ТС]
Мысль от Wolfdp мне понравилась, попробую применить у себя в коде.
0
Эксперт JavaЭксперт по электроникеЭксперт .NET
 Аватар для wizard41
3383 / 2698 / 573
Регистрация: 04.09.2018
Сообщений: 8,506
Записей в блоге: 3
27.06.2025, 23:19
qwerty.123, скорее вопрос в том, как грамотно прокинуть событие исключения и куда. Это очень частая ошибка, между прочим, и не только у не опытных.
Частенько вижу код задач, в которых исключения выбрасываются "в никуда". И другой пример: некий сервис сам обрабатывает свои исключения, но информация об этом никуда не отправляется, аля: !throw, зато внешний код тоже вызывает этот сервис через try/catch, но что он там получает при ошибке - не понятно. И вроде бы "двойная" защита, а толку ноль.
0
 Аватар для qwerty.123
19 / 18 / 1
Регистрация: 25.05.2025
Сообщений: 39
28.06.2025, 10:51
Цитата Сообщение от wizard41 Посмотреть сообщение
qwerty.123, скорее вопрос в том, как грамотно прокинуть событие исключения и куда.
По хорошему, если метод предназначен для многократного использования, причём разными частями приложения, то код такого метода, зачастую, не должен делать никаких предположений о контексте, в котором он был вызван. Если что-то пошло не так, то лучше чтобы такой метод просто генерировал исключение, содержащее подробную информаци об ошибке. А в вызывающем коде это исключение должно быть перехвачено через try/catch. Если в вызывающем коде не понятно, что делать с ошибкой, то он должен передавать её далее, просто генерируя throw в блоке catch. А если знает, то должен обработать ошибку, при этом залогировав её (если это нужно). Логировать ошибку нужно только один раз, чтобы не получилось, что один и тот же экземпляр полученного исключения был залогирован на каждом уровне, на который он был проброшен далее.

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

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

Логировать каждую ошибку всегда следует с использованием области логирования (log scope), добавляя в неё дополнительную информацию, которая потребуется при исследовании ошибки.
0
178 / 33 / 17
Регистрация: 02.02.2014
Сообщений: 373
05.07.2025, 11:22
Цитата Сообщение от HF Посмотреть сообщение
Чем сложнее приложение, тем меньше возможности иметь универсальный обработчик.
Ну, я сталкивался с подобной задачей. Решал так:
1) Сделал инфраструктуру для обработки ошибок
2) На главный поток окучил AppDomain.CurrentDomain.UnhandledExcepti on
3) Сделал собственную обёртку Thread
4) Создал с нуля собственную реализацию Taskов с гибким управлением всем, в том числе, обработкой ошибок
5) Сделал свои методы расширения для вызова лямбд и событий

и уже на этом всём построен софт. Это страшный гемор, но зато он реально отрабатывает все исключения и не падает из-за них, только логирует. Бонусом идёт трекинг и лёгкий дебаг где какой поток куда запустился, кто какую задачу ждёт и где какой деадлок. В некоторых случаях оно само деадлоки распутывает и разблокирует. Грохнуться может только из-за низкоуровневых косяков типа DirectX/C++, или из-за нехватки памяти. Так что эта задача решаемая, но стоит 10 раз подумать, стоит ли в это ввязываться. Для маленьких проектов это делать не надо.
0
Эксперт JavaЭксперт по электроникеЭксперт .NET
 Аватар для wizard41
3383 / 2698 / 573
Регистрация: 04.09.2018
Сообщений: 8,506
Записей в блоге: 3
05.07.2025, 12:19
Цитата Сообщение от VBDUnit Посмотреть сообщение
и где какой деадлок. В некоторых случаях оно само деадлоки распутывает и разблокирует.
Дедлок - это уже состояние блокировки, которое "распутать" нельзя, потому так и называется. Если можно - значит это не дедлок, а вполне управляемый участок.
В дедлок программа входит из-за ошибок/невнимательности программиста, например, когда некий поток начинает ожидать чего-то бесконечно..
0
178 / 33 / 17
Регистрация: 02.02.2014
Сообщений: 373
05.07.2025, 12:44
Дедлок - это уже состояние блокировки, которое "распутать" нельзя, потому так и называется. Если можно - значит это не дедлок, а вполне управляемый участок.
Не совсем. Там прикол в том, примитивы синхронизации тоже свои И вместо Wait() там делается Wait(condition), внутри которого оно в цикле делает Wait() с таймаутом и чекает условие. Если оно поменялось (например, я объект, который должен чего-то там ждать, но поменалось условие - меня выгрузили), то ожидание прерывается. Плюс на все Wait и Set/Reset и прочие семафорсколоковские штуки стоят трекеры на всяких [CallerMemberName/CallerFilePath], оно считает статистику где кто как вызывается и примерно с какой частотой кто кого ждет, и когда что-то идет не так, может просто втупую разблокировать какой-нибудь семафор. Это нарушает логику, но есть шанс что прокатит - ведь ошибки тоже глотаются и логируются.

В общем замороченная там штука внутри Это не чинит баги, само собой, но позволяет локализовать ущерб от них. Вместо "сломалось всё" у тебя получается "сломался такой-то кусочек".

Цитата Сообщение от wizard41 Посмотреть сообщение
В дедлок программа входит из-за ошибок/невнимательности программиста, например, когда некий поток начинает ожидать чего-то бесконечно..
Да. Но оно у меня насколько жирное, что везде всё отловить невозможно. Поэтому всё, до чего не дотянулись руки, хотя бы как-то купируется вот этой вот штуковиной.
0
Эксперт JavaЭксперт по электроникеЭксперт .NET
 Аватар для wizard41
3383 / 2698 / 573
Регистрация: 04.09.2018
Сообщений: 8,506
Записей в блоге: 3
05.07.2025, 12:52
VBDUnit, ну тут просто надо правильно манипулировать понятиями. В вышеописанном дедлока фактически нет - это просто управляемые задачи, скажем так. Если же произошел настоящий дедлок, то "выкрутиться" из него не получится.
Например, условная кнопка UI запускает некоторую Task-у и ждет ее результата по Task.Result. И сразу же встает на блокировку потока. Эта задача где-то там дальше выполнилась, вернула результат, но поток, вызвавший ее - уже залочен и просто не может ничего принять. Вот это и есть дедлок, который невозможно убрать. Только перезапуском всего приложения.
1
178 / 33 / 17
Регистрация: 02.02.2014
Сообщений: 373
05.07.2025, 13:30
Цитата Сообщение от wizard41 Посмотреть сообщение
Если же произошел настоящий дедлок, то "выкрутиться" из него не получится.
Например, условная кнопка UI запускает некоторую Task-у и ждет ее результата по Task.Result. И сразу же встает на блокировку потока. Эта задача где-то там дальше выполнилась, вернула результат, но поток, вызвавший ее - уже залочен и просто не может ничего принять. Вот это и есть дедлок, который невозможно убрать. Только перезапуском всего приложения.
По классике - да, Вы правы.

В моём случае - ожидание внутри Task палит, что оно в GUI-потоке, и просто разблокируется через 10 сек, вернув default. И пофиг ему, что таймаут не указан. Нельзя блокировать GUI-поток, просто нельзя. И далее конкретно для этого метода для ожидание таски таймаут будет снижен до секунды.

Если это был не GUI поток, то оно раз в секунду смотрит какие таски друг друга ждут по всему приложению и строит граф ожиданий. Если палит кольцо, то назначает таймаут, по истечении которого одно из ожиданий завершится с ошибкой.
0
 Аватар для IamRain
4693 / 2701 / 734
Регистрация: 02.08.2011
Сообщений: 7,226
05.07.2025, 15:23
Цитата Сообщение от VBDUnit Посмотреть сообщение
Создал с нуля собственную реализацию Taskов с гибким управлением всем, в том числе, обработкой ошибок
А чем тебя встроенная модель Task-ов не устроила?; как никак, наряду со Span-ами (как минимум), это один из столпов современного .NET-а.

Добавлено через 2 минуты
Просто сейчас, учитывая что ты героически deadlock-и "разруливал", складывается впечатление, что просто исправлялись свои же собственные адские костыли.
1
178 / 33 / 17
Регистрация: 02.02.2014
Сообщений: 373
05.07.2025, 16:44
Цитата Сообщение от IamRain Посмотреть сообщение
А чем тебя встроенная модель Task-ов не устроила?; как никак, наряду со Span-ами (как минимум), это один из столпов современного .NET-а.
Как минимум, в моём велосипеде:
1. Можно задавать ApartmentState STA/MTA
2. Можно задавать приоритет потока - разные пулы для каждого приоритета
3. Гарантия моментального старта (0 мс ожидания + в пуле всегда поддерживается несколько свободных потоков + пул не ограничен) - это вообще ключевое и самое важное
4. Опора на собственыне семафоры/ManualResetEventSlim и прочие штуки которые трекают ожидания и не дают виснуть GUI
5. Более удобный механизм отмены
6. Перехват всех исключений при компиляции в Release с автологированием
7. В дебаге задачи запоминают кто их создал (метод, файл, строка, имя и номер потока), кто к ним обращался и кто их ждёт
8. Подсчёт статистики и автоматический перехват мест, где создаётся лавинообразно много задач и их купирование

Ну и ещё много чего.

Цитата Сообщение от IamRain Посмотреть сообщение
Просто сейчас, учитывая что ты героически deadlock-и "разруливал", складывается впечатление, что просто исправлялись свои же собственные адские костыли.
Да я бы не сказал, там довольно продуманная архитектура, хотя легаси там тоже много. Просто оно ОЧЕНЬ большое, многопроцессное и +- реалтаймовое, и там параллельно происходит очень много разных штук, многие из которых требуют моментального отклика. Да и делалось это всё на .NET Framework 3.5 - .NET Framework 4.8, в те времена много полезных штук отсутствовало. Хотя под конец они там появились, те же Spanы.
1
 Аватар для IamRain
4693 / 2701 / 734
Регистрация: 02.08.2011
Сообщений: 7,226
05.07.2025, 17:09
Цитата Сообщение от VBDUnit Посмотреть сообщение
Да я бы не сказал, там довольно продуманная архитектура,
Ну ладно, хотя
Цитата Сообщение от VBDUnit Посмотреть сообщение
не дают виснуть GUI
выглядит сомнительно, так как приложения пишут всегда так, чтобы разгрузить поток GUI, таски для того и придумали (одно из применений - offloading основного потока).
И вроде с отменой проблем быть не должно, все довольно продумано.
Можно даже ловить forgotten exceptions в сторонних либах через TaskScheduler.UnobservedTaskException.
1
178 / 33 / 17
Регистрация: 02.02.2014
Сообщений: 373
05.07.2025, 17:29
Цитата Сообщение от IamRain Посмотреть сообщение
выглядит сомнительно, так как приложения пишут всегда так, чтобы разгрузить поток GUI, таски для того и придумали (одно из применений - offloading основного потока).
Я там всё так и сделал, чтобы не грузить GUI. Но прикол в том, что может быть кривой плагин, или пользователь где-то введёт кривой скрипт или кривое выражение, ну и по мелочи. Скриптовый язык там старается, конечно, ограничивать шаловливые ручки (например, на уровне интерпретатора запрещено вызывать Dispose у определённых объектов), но степеней свободы всё равно слишком много, всего не предусмотреть.

Цитата Сообщение от IamRain Посмотреть сообщение
Можно даже ловить forgotten exceptions в сторонних либах через TaskScheduler.UnobservedTaskException.
Да, приходилось пользоваться. Удобная штука.
0
Заблокирован
05.07.2025, 17:52
Какая то фантастика.
И какая цена за все это по производительности?
Спрашиваю как С++плюшник.
0
 Аватар для IamRain
4693 / 2701 / 734
Регистрация: 02.08.2011
Сообщений: 7,226
05.07.2025, 18:15
Цитата Сообщение от SmallEvil Посмотреть сообщение
Какая то фантастика.
Ну почему, в Rust в tokio тоже есть LocalThreadPool-ы где можно указывать число IO-потоков, число блокирующих потоков (CPU), при этом потоки которые сидят без дела автоматически паркуются, но это уже считается более ручным управлением. Больше писанины, но больше контроля.
А .NET традиционно всегда дает оптимальный дефолт, но не во всех сценариях гибок.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
05.07.2025, 18:15
Помогаю со студенческими работами здесь

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

обработка исключений до начала работы WCF сервиса
WCF сервис лежит на ASP.NET Development сервере. в сервисе определен примитивный метод: public...

Обработка исключений в серверном javascript
есть ли? в клиентском то есть, но надо в серверном. ведь из за отсутствия обработки исключений...

Обработка исключений при вводе числа, меньшего единицы
Как можно обработать исключение, чтобы при вводе числа меньше 1, выдалось сообщение? Добавлено...

Обработка исключений при вводе в TextBox недопустимых символов
Как добавить обработку с помощью try catch чтобы если вводим пустое значение в textBox1 выдавало...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Access
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru