uz
Feedback
C# (C Sharp) programming

C# (C Sharp) programming

Kanalga Telegram’da o‘tish

По всем вопросам- @notxxx1 Реестр РКН: https://clck.ru/3Fk3kb #VRHSZ

Ko'proq ko'rsatish

📈 Telegram kanali C# (C Sharp) programming analitikasi

C# (C Sharp) programming (@csharp_ci) Rus til segmentidagi kanali faol ishtirokchi. Hozirda hamjamiyat 18 306 obunachidan iborat bo'lib, Texnologiyalar & Aralashmalar toifasida 7 332-o'rinni va Rossiya mintaqasida 36 865-o'rinni egallagan.

📊 Auditoriya ko‘rsatkichlari va dinamika

невідомо sanasidan buyon loyiha tez o‘sib, 18 306 obunachiga ega bo‘ldi.

16 Iyun, 2026 dagi oxirgi ma’lumotlarga ko‘ra kanal barqaror faollikka ega. Oxirgi 30 kunda obunachilar soni -7 ga, so‘nggi 24 soatda esa -4 ga o‘zgardi va umumiy qamrov yuqori darajada qolmoqda.

  • Tasdiqlash holati: Tasdiqlanmagan
  • Jalb etish (ER): Auditoriya o‘rtacha 19.58% darajada jalb etiladi. Nashrdan keyingi dastlabki 24 soatda kontent odatda umumiy obunachilar sonining 7.47% ini tashkil etuvchi reaksiyalarni to‘playdi.
  • Post qamrovi: Har bir post o‘rtacha 3 584 marta ko‘riladi; birinchi sutkada odatda 1 368 ta ko‘rish yig‘iladi.
  • Reaksiyalar va o‘zaro ta’sir: Auditoriya faol: har bir postga o‘rtacha 0 ta reaksiya keladi.
  • Tematik yo‘nalishlar: Kontent .net, api, логика, архитектура, string kabi asosiy mavzularga jamlangan.

📝 Tavsif va kontent siyosati

Muallif resursni shaxsiy fikrni ifoda etish maydoni sifatida ta’riflaydi:
По всем вопросам- @notxxx1 Реестр РКН: https://clck.ru/3Fk3kb #VRHSZ

Yuqori yangilanish chastotasi (oxirgi ma’lumot 17 Iyun, 2026 da olingan) sababli kanal doimo dolzarb va katta qamrovli bo‘lib qoladi. Analitika auditoriya kontent bilan faol hamkorlik qilishini, uni Texnologiyalar & Aralashmalar toifasidagi muhim ta’sir nuqtasiga aylantirishini ko‘rsatadi.

18 306
Obunachilar
-424 soatlar
+137 kunlar
-730 kunlar
Postlar arxiv
«А что там собственно нового в C# 12?» Интересное видео, в котором Евгений Федотов рассказывает о нововведениях в C# 12 О чём
+3
«А что там собственно нового в C# 12?» Интересное видео, в котором Евгений Федотов рассказывает о нововведениях в C# 12 О чём вообще речь? ⏩Упрощение определения типов, использование точки с запятой вместо фигурных скобок. ⏩Collection-оператор, объединение массивов в одну коллекцию. ⏩Атрибут эксперимента, для указания на экспериментальный код. ⏩Атрибут для перехвата методов. ⏩Использование перехватчиков для логирования и других целей. ⏩Возможность указывать функциональные значения для лямда-выражений. ⏩Возможность выдавать псевдоним любому типу или кортежу. ⏩и ещё освещается много всего, так что будет полезно 📎 Кликабельный план видео 📎 Видео @csharp_ci

🖥 Как на самом деле работает Async/Await в C# Держите полезную статью о том, как реализована асинхронность в C#. О чём стать
🖥 Как на самом деле работает Async/Await в C# Держите полезную статью о том, как реализована асинхронность в C#. О чём статья? ⏩Статья обсуждает различия в реализации асинхронных методов в .NET Framework и .NET Core. ⏩В .NET Framework выделяется много объектов, связанных с асинхронными вызовами, что приводит к большому объему памяти. ⏩В .NET Core реализация асинхронных методов значительно изменилась, что привело к уменьшению выделения памяти. ⏩ExecutionContext в .NET Core теперь является неизменяемым, что упрощает передачу контекста и делает его передачу более распространенной. ⏩Логический CallContext в .NET Core больше не существует, а его функции моделируются через AsyncLocal<T> ⏩В .NET Core глобальная очередь ThreadPool реализована как ConcurrentQueue<T>, что позволяет оптимизировать выделение памяти. ⏩Реализация метода AwaitUnsafeOnCompleted в .NET Core отличается от .NET Framework, что приводит к более эффективному использованию памяти. 📎 Статья 📎 На английском @csharp_ci

Устал писать тесты с кучей моков? Сложно поддерживать приложение, ведь бизнес логика перемешалась с инфраструктурой? 😰 Присо
Устал писать тесты с кучей моков? Сложно поддерживать приложение, ведь бизнес логика перемешалась с инфраструктурой? 😰 Присоединяйся к нашему практическому курсу по Domain Driven Design, где за 10 вебинаров мы разложим все по полочкам. 🚀Стартуем 1 апреля. Присоединяйся прямо сейчас https://microarch.ru/courses/hexagonal-architecture?utm_source=posev&utm_medium=erid:2VtzqwtV8Fo&utm_campaign=13 Наш курс по Domain-Driven Design позволит вам изучить основы и принципы этого подхода, а также освоить практические навыки, необходимые для успешного применения DDD в реальных проектах. Вы узнаете: ✅ Что такое Domain-Driven Design и почему он так важен для современных разработчиков. ✅ Основы проектирования доменов и формирование предметной области. ✅ Принципы и методы моделирования предметной области, включая разработку моделей предметной области и определение бизнес-правил. ✅ Создание эффективных и масштабируемых сервисов с использованием DDD. ✅ Применение шаблонов проектирования и принципов DDD для оптимизации кода и повышения производительности. Курс ведет действующий архитектор и разработчик Кирилл Ветчинкин. Он успешно реализовал проекты для Мегафона, Теле2, ВСS Brокer. Постоянный спикер крупных IT-конференций. Не упусти шанс стать экспертом! Присоединяйся к нашему курсу и раскрой свой потенциал в разработке успешных проектов уже сегодня https://microarch.ru/courses/hexagonal-architecture?utm_source=posev&utm_medium=erid:2VtzqwtV8Fo&utm_campaign=13 Реклама. ИП Ветчинкин К.Е. ИНН: 773376451099 Erid: 2VtzqwtV8Fo

🖥 Интерактивный учебник по C# Возможно, вы в курсе, что этот учебник существует. В любом случае, это отличный способ ознаком
+2
🖥 Интерактивный учебник по C# Возможно, вы в курсе, что этот учебник существует. В любом случае, это отличный способ ознакомиться с C# и .NET Пока что заданий в нём не много, а оглавление выглядит так: — Запуск первой программы C# — Объявление и использование переменных — Работа со строками — Другие действия со строками — Строки поиска — Выполнение задачи Учебник будет развиваться и пополняться новыми заданиями, что очень неплохо 📎 Учебник @csharp_ci

❓Как С#-разработчику вырасти до уровня Senior в 2024? Изучить .NET Framework, Scrum-методики и многое другое на онлайн-курсе
❓Как С#-разработчику вырасти до уровня Senior в 2024? Изучить .NET Framework, Scrum-методики и многое другое на онлайн-курсе «C# Developer. Professional» от OTUS. Программа идеально подходит для начинающих C#-разработчиков, которые хотят развиваться в серверной разработке, создании приложений и микросервисов. На курсе вас ждут: - живые вебинары с возможностью задать вопросы экспертам; - много практики и сильные проекты для портфолио; - поддержка профессионального комьюнити. 👉 Пройдите бесплатный тест прямо сейчас и узнайте, готовы ли вы сделать следующий шаг в карьере: https://otus.pw/Wszi7/?erid=LjN8K2gXC Реклама. ООО "ОТУС ОНЛАЙН-ОБРАЗОВАНИЕ". ИНН 9705100963.

🖥 Годное видео о рефакторинге кода C# В частности, в ролике идёт речь о таких вещах как: ⏩Переименование переменных и методо
+2
🖥 Годное видео о рефакторинге кода C# В частности, в ролике идёт речь о таких вещах как: ⏩Переименование переменных и методов — Улучшает ясность и понятность кода, делая его самодокументируемым. Пример: система управления контентом, где переменные и методы не дают ясного понимания их назначения.Извлечение интерфейса — Разделение определения операции от ее реализации, улучшение модульности, упрощение тестирования и увеличение гибкости кода. Пример: система обработки платежей, где класс реализует методы для каждого типа платежа, но добавление новых способов обработки может быть проблематичным.Упрощение условных выражений — Улучшение читаемости и поддержки кода, избегая сложных и больших условных выражений. Пример: система управления заказами, где много уровней вложенности и проверок, что затрудняет чтение и понимание кода. 📎 Кликабельный план ролика 📎 Youtube @csharp_ci

🦾Хардкорный тест по языку С🦾 📌Пройдите полный тест из 20 вопросов на сайте и проверьте, насколько вы готовы к обучению на
🦾Хардкорный тест по языку С🦾 📌Пройдите полный тест из 20 вопросов на сайте и проверьте, насколько вы готовы к обучению на углубленном курсе - «Программист С» от OTUS. Сможете сдать - пройдете на курс с финальной скидкой 10% по промокоду devc10 ⏰ Время прохождения теста ограничено 30 минут 👉ПРОЙТИ ТЕСТ

🖥Что такое NullReferenceException, и как исправить? Итак кейс: выполняется некоторый код, выбрасывается исключение NullRefer
🖥Что такое NullReferenceException, и как исправить? Итак кейс: выполняется некоторый код, выбрасывается исключение NullReferenceException со следующим сообщением:
Object reference not set to an instance of an object (В экземпляре объекта не задана ссылка на объект)
Что же это значит, и как исправить код? ⏩В двух словах Вы пытаетесь воспользоваться чем-то, что равно null (или Nothing в VB.NET). Это означает, что либо вы присвоили это значение, либо вы ничего не присваивали. Как и любое другое значение, null может передаваться от объекта к объекту, от метода к методу. Если нечто равно null в методе "А", вполне может быть, что метод "В" передал это значение в метод "А". ⏩Более подробно Если среда выполнения выбрасывает NullReferenceException, то это всегда означает: вы пытаетесь воспользоваться ссылкой. И эта ссылка не инициализирована (или уже не инициализирована). Это означает, что ссылка равна null, а вы не сможете вызвать методы через ссылку, равную null, как тут:
string foo = null;
foo.ToUpper();
Этот код выбросит исключение NullReferenceException на 2 строке, потому что вы не можете вызвать метод ToUpper() у ссылки на string, равной null. ⏩Отладка Как определить источник ошибки? Общие рекомендации: поставьте точки останова в ключевых местах, изучите значения переменных, расположив курсор мыши над переменной, либо открыв панели для отладки: Watch, Locals, Autos. Если вы хотите определить место, где значение ссылки устанавливается (или нет), нажмите ПКМ на её имени и выберите "Find All References". Затем вы можете поставить точки останова на каждой найденной строке и запустить приложение в режиме отладки. Каждый раз, когда отладчик остановится на точке останова, вы можете удостовериться, что значение верное. Так вы придёте к месту, где значение ссылки не должно быть null, и определите, почему не присвоено верное значение. 📎 Очень подробное обсуждение этой проблемы с примерами @csharp_ci

erid: LjN8K3218 ❓Как С#-разработчику вырасти до уровня Senior в 2024? Изучить .NET Framework, Scrum-методики и многое другое
erid: LjN8K3218 ❓Как С#-разработчику вырасти до уровня Senior в 2024? Изучить .NET Framework, Scrum-методики и многое другое на онлайн-курсе «C# Developer. Professional» от OTUS. Программа идеально подходит для начинающих C#-разработчиков, которые хотят развиваться в серверной разработке, создании приложений и микросервисов. На курсе вас ждут: - живые вебинары с возможностью задать вопросы экспертам; - много практики и сильные проекты для портфолио; - поддержка профессионального комьюнити. 👉 Пройдите бесплатный тест прямо сейчас и узнайте, готовы ли вы сделать следующий шаг в карьере: https://otus.pw/SXyf/ Реклама. ООО "ОТУС ОНЛАЙН-ОБРАЗОВАНИЕ". ИНН 9705100963.

🖥 Обработка ошибок с помощью IExceptionHandler в ASP.NET Core 8.0 Относительно недавно на свет появился ASP.NET Core 8.0, и
🖥 Обработка ошибок с помощью IExceptionHandler в ASP.NET Core 8.0 Относительно недавно на свет появился ASP.NET Core 8.0, и теперь можно обрабатывать ошибки с помощью IExceptionHandler. А вот и полезная статья о том, как это делать. ⏩Вкратце о IExceptionHandler IExceptionHandler — это интерфейс, который предоставляет разработчику обратный вызов для обработки известных исключений в центральном расположении. IExceptionHandler реализации регистрируются путем вызова IServiceCollection.AddExceptionHandler. Время существования экземпляра IExceptionHandler — одноэлементное. Можно добавить несколько реализаций, и они вызываются в порядке регистрации. Если обработчик исключений обрабатывает запрос, он может вернуться true к остановке обработки. Если исключение не обрабатывается обработчиком исключений, то элемент управления возвращается к поведению по умолчанию и параметрам из по промежуточного слоя. Для обработки и необработанных исключений создаются различные метрики и журналы. 📎 Статья 📎 Доки от Windows, как обрабатывать ошибки @csharp_ci

Сможете разгадать тайны Гиперкуба? Проявите смекалку и примените ИТ-скиллы на Tinkoff CTF — соревновании по спортивному хакин
Сможете разгадать тайны Гиперкуба? Проявите смекалку и примените ИТ-скиллы на Tinkoff CTF — соревновании по спортивному хакингу. Победителям — призы до 420 000 ₽. Вот как все устроено: — Выберите лигу: опытных или новых игроков. Задания рассчитаны на ИТ-специалистов уровня мидл и выше. Если играете впервые, можете потренироваться с демоверсией заданий. — Соревнуйтесь в одиночку или командой до трех человек. Организаторы помогут найти команду, если нет своей. — Играйте онлайн или офлайн — в одном из 16 городов России, Беларуси и Казахстана. — Используйте время на решение заданий или общение с участниками — у вас будет 36 часов. Задания будут интересны разработчикам, QA- и SRE-инженерам, аналитикам и другим опытным ИТ-специалистам. Зарегистрируйтесь до 19 апреля и пробуйте свои силы erid:2VtzqwMPLoX Реклама. АО "Тинькофф Банк", ИНН 7710140679, лицензия ЦБ РФ № 2673

🖥 Как "схлопнуть" вызовы, чтобы вызываемый код не "захлебнулся"? ▶️Итак, сценарий такой. В коде генерируются некие обновления, которые затем обрабатываются неким методом. Обновления могут генерироваться слишком часто, плюс метод обработки может тормозить, в результате обновления не успевают обрабатываться, накапливаются, обработчик потом делает лишнюю работу. Хотелось бы как-то "накопить" обновления, пока обработчик занят. Причём, сами данные для обновления уже "накапливаются", об этом заботиться не нужно, нужно только разрулить "накопление" запросов к обработчику, чтобы когда обработчик освободиться обрабатывать не все накопившиеся запросы на обновление по отдельности, а за один раз сразу весь пакет "обновлений" обработать. Вот так происходит сейчас:
async Task UpdateFoo()
{   ...
    await SaveAsync();
}

async Task UpdateBar()
{   ...
    await SaveAsync();
}

async Task UpdateBaz()
{   ...
    await SaveAsync();
}

async Task SaveAsync()
{   // здесь нужно сделать так, 
    // чтобы одновременно обрабатывался только 1 запрос
    // по окончании которого проверялось бы не было ли ещё запросов
    // и если были, то обновление запускалось бы ещё 1 раз (сразу за всё "накопленное")
    // и так пока есть обновления по окончании очередной обработки
}
Как это можно нормально написать? Для того, чтобы зайти один раз — понятно, SemaphoreSlim, видимо (с WaitAsync). А вот остальное как лучше сделать? ▶️Вариант решения. ⏩Общий пример, берём System.Threading.Channels.Channel<T>:
private readonly Channel<T> _channel = Channel<T>.CreateUnbounded();
⏩Пишем метод разрегребания:
private Task _workerTask;

private async Task WorkerAsync()
{
    ChannelReader<T> reader = _channel.Reader;
    List<T> list = new();

    // ждём здесь, если в канале пусто
    while (await reader.WaitToReadAsync())
    {
        // забираем всё что есть
        // ну или можно счётчиком ограничить максимальное количество выгребаемых данных за раз
        while (reader.TryRead(out T data))
        {
            list.Add(data);
        }
        // пачка собрана, погнали. это можно в try-catch завернуть, чтобы воркер не падал
        await UseAsync(list);
        list.Clear();
    }
}
⏩Стартуем воркер:
_workerTask = WorkerAsync();
Чтобы закинуть в канал:
_channel.Writer.TryWrite(x);
⏩Чтобы закрыть канал (насовсем), и чтобы метод WorkerAsync завершился, нужно вызвать:
_channel.Writer.Complete();
await _workerTask;
Вот и все дела 🙃 @csharp_ci

Ozon Tech приглашает на C#-митап 29 марта | 19:00 Офлайн в Москве | Онлайн Какие темы в программе? #️⃣ ThreadPool, выявление
Ozon Tech приглашает на C#-митап 29 марта | 19:00 Офлайн в Москве | Онлайн Какие темы в программе? #️⃣ ThreadPool, выявление проблем при работе на высоких нагрузках, Алексей Калдузов, руководитель группы разработки C#-платформы. #️⃣ Причины медленной работы БД и способы оптимизации на примере PostgreSQL, Дмитрий Орлов, эксперт разработки, команда Metazon Inventory. #️⃣ Решение проблем шардированных БД, Артём Барабошин, старший C#-разработчик. А после докладов — уютный вечер за разговорами🍕 Зарегистрируйтесь, чтобы получить офлайн-билет или присоединиться к трансляции. erid: LjN8Jtu2A Реклама. Рекламодатель ООО «Озон Технологии».

Что выведет на экран это код?
Anonymous voting

#ПятничныйКвиз
#ПятничныйКвиз

🖥 Ловите полезные ссылки для изучения асинхронности в C# Асинхронность вызывает большие проблемы у многих, но выход есть — в
🖥 Ловите полезные ссылки для изучения асинхронности в C# Асинхронность вызывает большие проблемы у многих, но выход есть — вот: *️⃣ Во-первых, стоит прочитать весь блок статей на MSDN *️⃣ Первый гигант async/await — Stephen Cleary. Вводная статья *️⃣ Статья об устройстве async/await под капотом. Не обязательно заучивать всю машину состояний под await, но тут как с блоком итератора yield - код пишется, а руки трясутся. *️⃣ На этом этапе может начаться каша в голове и встреча с SynchronizationContext. Начать можно с этой статьи на MSDN. Но если она покажется душной, переходите к пункту 5. *️⃣ Второй гигант, и тоже Stephen. Я советую перечитать все статьи обоих, что можно найти. Но продолжая тему контекста синхронизации — эта статья крайне важна для тех, кто тренируется в консольных приложениях. Вопрос об асинхронности/многопоточности уходит после нее. И небольшой, но классный ответ на StackOverflow о TaskScheduler. *️⃣ Но если вопрос все же не ушел — There is no thread. Также советую загуглить словосочетание из этой статьи — naturally-asynchronous operations. *️⃣ Для закрепления пунктов 4,5,6 можно почитать о Task.Run (и комплексных случаях использования многопоточности и асинхронности). Внутри этого блока статей много полезных ссылок и на другие материалы. *️⃣ Лучшие практики от Cleary. Кстати, у него есть книга по асинхронности, можно ознакомиться при желании. *️⃣ Обработка исключений. @csharp_ci

🖥 Задачка с собеседования от Сбера: Вставьте код функции Mutate (без использования ключевого слова unsafe), чтобы на консоль вывелось «404». Сигнатуру функции не менять, замыкания не ловить

const string constStr = "000";

Mutate(constStr);

var nonConst = "000";

Console.WriteLine(nonConst);



void Mutate(string str)

{

    // write your code here

}
Вот пример моего решения, которое полностью устроило лида, который меня собесил

void Mutate(string str)
{
    GCHandle handle = GCHandle.Alloc(str, GCHandleType.Pinned);

    IntPtr pointer = handle.AddrOfPinnedObject();

    Marshal.WriteByte(pointer, 0, (byte)'4');
    Marshal.WriteByte(pointer, 4, (byte)'4');

    handle.Free();
}
Весь смысл кроется в том, как работают строки в .net (интернирование), что такое pinned object и как работать с unmanaged кодом. Идея интернирования строк состоит в том, чтобы хранить в памяти только один экземпляр типа String для идентичных строк. При старте нашего приложения виртуальная машина создаёт внутреннюю хэш-таблицу, которая называется таблицей интернирования (иногда можно встретить название String Pool). Pinned Object Heap (POH, Куча Закрепленных Объектов). В отличие от других видов кучи, эта доступна разработчикам явно (что не характерно для сборщика мусора). @csharp_ci

🖥 Humanizer C# Humanizer — бесплатная .NET-библиотека с открытым исходным кодом, которая предлагает набор методов для манипулирования строками, числами, датами, временем, временными интервалами, цифрами и величинами в удобном для человеческого восприятия виде. Работа со временем и датой — одна из самых распространенных и муторных задач в любом приложении. Пользователи ожидают увидеть дату и время в формате, который будет легко восприниматься и соответствовать контексту. 🔵Форматирование DateTime в удобочитаемый вид Один из самых простых способов гуманизировать объект DateTime — использовать метод расширения Humanize, который возвращает время и дату на естественном языке. Например:

using Humanizer;

DateTime now = DateTime.Now;
DateTime yesterday = now.AddDays(-1);
DateTime tomorrow = now.AddDays(1);
DateTime nextWeek = now.AddDays(7);

Console.WriteLine(now.Humanize()); // сейчас
Console.WriteLine(yesterday.Humanize()); // вчера
Console.WriteLine(tomorrow.Humanize()); // через 23 часа
Console.WriteLine(nextWeek.Humanize()); // через 6 дней
Как видите, метод Humanize возвращает строку, которая легко воспринимается и зависит от текущих времени и даты. Он также учитывает различные временные отношения, такие как сегодня, вчера, завтра, а также будущие или прошлые даты. 🔵Еще один способ гуманизации объекта DateTime — использовать метод расширения Humanize с булевым параметром, указывающим, отображать относительное время или нет. Относительное время — это строка, которая представляет, сколько времени прошло с или пройдет до конкретного момента. Например:

using Humanizer;

DateTime anHourAgo = DateTime.Now.AddHours(-1);
DateTime anHourLater = DateTime.Now.AddHours(1);

Console.WriteLine(anHourAgo.Humanize()); // час назад
Console.WriteLine(anHourLater.Humanize()); // через 59 минут
🔵Как-то вот так можно использовать Humanizer для управления датой и временем. 📎 Подробнее можно почитать тут @csharp_ci

💼 Если ваши сотрудники на удалёнке, так ещё и в разных часовых поясах, то вам явно тяжело выстроить рабочий процесс и сохран
💼 Если ваши сотрудники на удалёнке, так ещё и в разных часовых поясах, то вам явно тяжело выстроить рабочий процесс и сохранить максимум эффективности. Вам может помочь виртуальное пространство от Яндекс 360. На вебинаре 20 марта в 11:00 по Москве они как раз расскажут, как лучше управлять командой в такой ситуации! Зарегистрироваться можно здесь 👈

🖥 Как распарсить HTML в .NET? 🔜 Итак, реальный кейс: Необходимо извлечь все URL из атрибутов href тегов a в HTML странице.
🖥 Как распарсить HTML в .NET? 🔜 Итак, реальный кейс: Необходимо извлечь все URL из атрибутов href тегов a в HTML странице. Можно попробовать воспользоваться регулярными выражениями:
Uri uri = new Uri("http://google.com/search?q=test");
Regex reHref = new Regex(@"<a[^>]+href=""([^""]+)""[^>]+>");
string html = new WebClient().DownloadString(uri);
foreach (Match match in reHref.Matches(html))
    Console.WriteLine(match.Groups[1].ToString());
Но возникает множество потенциальных проблем: — Как отфильтровать только специфические ссылки, например, по CSS классу? — Что будет, если кавычки у атрибута другие? — Что будет, если вокруг знака равенства пробелы? — Что будет, если кусок страницы закомментирован? — Что будет, если попадётся кусок JavaScript? — И так далее. Регулярное выражение очень быстро становится нечитаемым. Какие есть другие варианты? 🔜 Для парсинга HTML используйте AngleSharp Проверенный игрок на поле парсеров. В отличие от CsQuery, написан с нуля вручную на C#. Также включает парсеры других языков. API построен на базе официальной спецификации по JavaScript HTML DOM. Изначально содержал в некоторых местах странности, непривычные для разработчиков на .NET (например, при обращении к неверному индексу в коллекции будет возвращён null, а не выброшено исключение), но разработчик в конце концов сдался и исправил самые жуткие костыли. Что-то ушло само, например, Microsoft BCL Portability Pack. Что-то осталось, например, пространства имён очень гранулярные, даже базовое использование библиотеки требует три using и т. п.), но в целом ничего критичного. Обработка HTML простая, к примеру вот:
IHtmlDocument angle = new HtmlParser().ParseDocument(html);
foreach (IElement element in angle.QuerySelectorAll("a"))
    Console.WriteLine(element.GetAttribute("href"));
📎 Читать подробнее @csharp_ci