.NET Разработчик
6.51K subscribers
427 photos
2 videos
14 files
2.04K links
Дневник сертифицированного .NET разработчика.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День 1711. #ЗаметкиНаПолях #Debugging
Правила Отладки: Заставь Его Упасть. Окончание
Начало

Неконтролируемое состояние
Задача «дать ему упасть» становится значительно сложнее, когда сбой происходит периодически. Хотя вы можете точно понять, какие шаги привели к первоначальной неудаче, последовательное её воспроизведение остаётся невозможным – возможно, это происходит только раз из 5, 10 или 100 попыток.

Следует признать, что, несмотря на понимание причин, о вам не хватает исчерпывающих знаний обо всех точных условиях. Овладение этими разнообразными условиями позволит вам постоянно вызывать сбой. В ситуациях, когда сбой непостоянен, вы должны тщательно проанализировать его, не обращая внимания на многочисленные случаи, когда его не возникло. Основная стратегия предполагает сбор полных данных во время каждого запуска, что позволяет провести углублённый анализ после подтверждения сбоя. Этого можно достичь, генерируя обширные выходные данные системы во время её работы и собирая эту информацию в специальном «журнале отладки».

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

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

Несомненно, случайность усложняет процесс подтверждения исправления. Например, если тест показывает процент неудач в 10%, а ваше вмешательство снижает его до 3% — но вы прекращаете тестирование после 28 попыток — вы можете поверить, что проблема решена, даже если это не так.

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

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

Знайте, что «это» может случиться

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

Итого
Если вы понятия не имеете, что может быть не так, жизнь становится сложнее! Начните с того, что убедитесь, что вы можете обеспечить постоянное проявление ошибки. Потратьте время на подготовку входных данных и настройку параметров, которые последовательно вызывают проблему. Затем упакуйте эти шаги в упрощённую процедуру, которая облегчает лёгкое и автоматизированное выполнение. Когда вы имеете дело со сложной ошибкой, вы обнаружите, что неоднократно воспроизводите её по мере выяснения основной причины. Таким образом, оптимизация процесса воспроизведения в итоге сэкономит вам время. Столкнувшись с ошибкой, которая возникает случайно, приложите усилия к пониманию причин её случайного характера.

Источник: https://dev.to/rajasegar/debugging-rules-make-it-fail-33c0
👍6
День 1712. #ЧтоНовенького
Windows Dev Drive
Dev Drive — это новая форма хранилища в Windows 11, позволяющая повысить производительность ключевых рабочих нагрузок разработчиков.

Dev Drive использует технологию ReFS для целевой оптимизации файловой системы и обеспечения большего контроля над настройками тома хранилища и безопасностью, включая определение доверия, настройку антивируса и административный контроль над тем, какие фильтры подключены. Согласно бенчмаркам Microsoft, это обеспечивает до 30% прироста производительности сборок и других диско-нагруженных операций.

Windows использует NTFS для системного диска и по умолчанию для большинства несъёмных дисков. Устойчивая файловая система (Resilient File System - ReFS) — это новый формат файловой системы от Microsoft, разработанный для обеспечения максимальной доступности данных, эффективного масштабирования для больших наборов данных при различных рабочих нагрузках и обеспечения целостности данных и устойчивости к повреждению. Он направлен на решение расширяющегося набора сценариев хранения данных и создание основы для будущих инноваций.

Что хранить на Dev Drive?
- Репозитории исходного кода и файлы проекта
- Кэши пакетов
- Артефакты сборки и промежуточные файлы

Dev Drive не предназначен для хранения инструментов разработчика, таких как:
- Visual Studio
- MSBuild
- .NET SDK
- Windows SDK, etc.
Эти утилиты должны размещаться на основном диске C:\.

Странно, что ничего не сказано про файлы баз данных, которые теоретически должны подходить под диско-нагруженные операции. Однако, я не смог найти советов ни за, ни против их размещения на Dev Drive. Если кто-нибудь что-нибудь об этом знает или был опыт, отпишитесь в комментариях.

Риски безопасности и доверия в отношении Dev Drive
Обычно существует компромисс между производительностью и безопасностью. Использование Dev Drive передаёт контроль над этим балансом в руки разработчиков и администраторов по безопасности, которые несут ответственность за выбор прикреплённых фильтров и параметров сканирования Microsoft Defender.

Антивирусные фильтры по умолчанию включены для Dev Drive. Microsoft Defender по умолчанию использует новый параметр performance mode (режим производительности) на Dev Drive, делая акцент на производительности и обеспечивая при этом безопасную альтернативу простому исключению папок из-под анализа антивируса.

«Доверенный» Dev Drive
Диски Dev Drive автоматически обозначаются как доверенные с использованием флага, хранящегося в системном реестре во время исходного форматирования, что по умолчанию обеспечивает максимально возможную производительность. Доверие диску означает, что разработчик, использующий этот том, полностью уверен в безопасности хранящегося там контента. Разработчик берёт на себя ответственность за управление безопасностью хранимого контента, чтобы получить дополнительную производительность.

Dev Drive, помеченный как доверенный, является сигналом для Microsoft Defender для работы в режиме производительности. Это обеспечивает баланс между защитой от угроз и производительностью. Для повышения уровня защиты Microsoft Defender также предлагает «Режим защиты в реальном времени» (Real-time protection mode). Защита в режиме реального времени по-прежнему будет включена на всех остальных томах.

Источники:
-
https://blogs.windows.com/windowsdeveloper/2023/06/01/dev-drive-performance-security-and-control-for-developers/
-
https://learn.microsoft.com/en-us/windows/dev-drive/
👍10
День 1713. #ВопросыНаСобеседовании #Многопоточность
Самые часто задаваемые вопросы на собеседовании по C#

24. Чем задачи в C# отличаются от традиционных потоков? Объясните преимущества и сценарии, в которых задачи предпочтительнее прямого создания потоков.

И задачи (Task), и потоки (Thread) используются в C# для асинхронного и параллельного программирования. Однако между ними есть некоторые ключевые различия:
1) Уровень абстракции.
Задачи представляют собой абстракцию более высокого уровня, построенную на основе потоков и фокусирующуюся на выполняемой работе, а не на низкоуровневом управлении потоками. Потоки — это концепция более низкого уровня, позволяющая более детально контролировать детали выполнения.

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

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

4) Асинхронное программирование.
Задачи интегрируются с шаблоном async/await для упрощения асинхронного программирования. Потоки требуют ручной синхронизации при координации с асинхронными операциями.

5) Продолжения.
Задачи позволяют упростить цепочку работ с помощью ContinueWith(), позволяя планировать выполнение работы после завершения предыдущей задачи. Для таких сценариев потоки требуют ручной синхронизации с использованием примитивов синхронизации.

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

7) Обработка исключений.
Задачи лучше обеспечивают обработку исключений, объединяя исключения из нескольких задач и распространяя их в вызывающий контекст. Для потоков требуются более сложные механизмы обработки исключений, возникающих в дочерних потоках.

Когда использовать задачи:
- При работе с асинхронными или параллельными рабочими нагрузками. Можно извлечь выгоду из улучшенного управления ресурсами и масштабируемости ThreadPool.
- При использовании шаблона async/await для асинхронного программирования.
- Необходима координация работы с использованием продолжений.
- Необходим встроенный механизм отмены и стандартизированная обработка исключений.

Когда использовать потоки:
Вам нужен детальный контроль над выполнением, или у вас особые требования, которые не могут быть удовлетворены с помощью абстракций более высокого уровня (Task):
- Долгоживущие единицы работы: фоновые сервисы или сложные расчеты, требующие большего контроля над выполнением работ.
- Детальный контроль над выполнением потоков: установка приоритета или тонкая настройка синхронизации и прерываний.
- Низкоуровневое программирование.
- Взаимодействие с неуправляемым кодом.

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

Источник: https://dev.to/bytehide/c-multithreading-interview-questions-and-answers-4opj
👍31
День 1714. #ЗаметкиНаПолях
Как Получить Значение Типа по Умолчанию во Время Выполнения
При написании кода можно использовать значение default(T). Но что, если вы хотите сделать то же самое во время выполнения? Как получить значение по умолчанию для любого System.Type? (Допустим, вам это понадобилось)

В сети можно найти множество неправильных ответов. Например, этот:
object? GetDefaultValue(Type t) => 
t.IsValueType
? Activator.CreateInstance(t)
: null;

Это отлично работает для ссылочных типов и большинства типов значений. Но Activator.CreateInstance(T) — это не то же самое, что default(T), когда T является типом значения. Activator.CreateInstance(T) аналогичен new T(). То есть, он вызывает конструктор по умолчанию. default(T) не вызывает конструктор по умолчанию. Он инициализирует все поля значениями по умолчанию.

В C# 10 вы можете создать структуру с конструктором по умолчанию:
struct Sample
{
public int Value { get; }
public Sample() => Value = 1;
}

Посмотрим на разницу между default(Sample) и new Sample():
default(Sample).Value // 0

new Sample().Value // 1

Activator
.CreateInstance<Sample>()
.Value // 1

Так как же получить default(T) во время выполнения?

1. Использовать System.Linq.Expression, но это медленно (~20мс):
object? GetDefaultValue(Type t)
{
if (t.IsValueType)
{
var defExpr = Expression.Default(t);
return Expression
.Lambda(defExpr)
.Compile()
.DynamicInvoke();
}
return null;
}

2. Обобщённый класс со статическим свойством, которое возвращает значение обобщённого типа по умолчанию. Это быстрее (~2мс):
object? GetDefaultValue(Type t)
{
if (t.IsValueType)
{
var defType = typeof(DefaultProvider<>)
.MakeGenericType(t);
return defType
.InvokeMember("Value",
BindingFlags.Public |
BindingFlags.Static |
BindingFlags.GetProperty,
binder: null,
target: null,
args: null,
culture: null);
}
return null;
}

private static class DefaultProvider<T>
{
public static T? Value => default;
}

3. Начиная с.NET Core, вы можете использовать System.Runtime.CompilerServices.RuntimeHelpers.GetUninitializedObject, чтобы получить неинициализированный экземпляр (~30нс):
object? GetDefaultValue(Type t)
{
if (t.IsValueType)
return RuntimeHelpers
.GetUninitializedObject(t);

return null;
}

Можно использовать любой из этих методов, чтобы получить значение по умолчанию:
((Sample)GetDefaultValue(typeof(Sample)))
.Value // 0

Источник: https://www.meziantou.net/get-the-default-value-of-a-type-at-runtime.htm
👍17
День 1715. #ЗаметкиНаПолях
Виды Структур в C#. Начало
Рассмотрим, в чём разница между структурой, структурой для чтения и ref-структурой, а также, какие ограничения есть у этих типов.

1. Структура
Все в той или иной степени ими пользовались. Структура — это тип значения, который в идеале должен представлять одно логическое значение: DateTime, int, double или TimeSpan, либо пользовательские, вроде Point2D, которая может состоять из координат x и y. В документации Microsoft есть подсказка, когда не следует использовать структуры: если экземпляр придётся часто упаковывать.

Обычно типы значений, такие как структура, находятся в стеке. Стек — это хороший способ моделирования времени жизни переменной. Если вы объявите int в функции, он будет жить только в этой области:
public void MyFunc()
{
int a = 2;
}
Здесь a существует только внутри MyFunc, и только на стеке. Но если мы используем какой-либо тип значения, например, в качестве поля в классе, тогда эта переменная должна находиться в куче, поскольку её родитель находится в куче.

Структуры позволяют многое из того, что допускает класс, но с некоторыми ключевыми отличиями:
- Структура не может наследовать от других классов или структур (возможны интерфейсы)
- Структура не может иметь деструктор.

Пример структуры:
public struct Color
{
public byte Red;
public byte Green;
public byte Blue;

public Color() {
Red = 0; Green = 0; Blue = 0;
}

public void ShiftToGrayscale()
{
var avg = (byte)((Red + Green + Blue) / 3);
Red = Green = Blue = avg;
}
}

2. Структура только для чтения
readonly struct были представлены в C# 7.2. Основная идея состоит в том, чтобы сделать структуру неизменяемой. Конечно, вы можете сделать это без модификатора только для чтения (например, DateTime существует с самого начала), но компилятор поможет вам, если вы нарушите логику только для чтения.

Пример выше выдаст несколько ошибок компилятора, если добавить readonly к объявлению структуры. Причина проста: во-первых, мы должны объявить все поля как readonly (иначе любой вызывающий объект мог бы просто изменить их). Во-вторых, метод ShiftToGrayscale по-прежнему изменяет внутреннее состояние, поэтому его тоже надо изменить на выдачу новой структуры:
public readonly struct Color
{
public readonly byte Red;
public readonly byte Green;
public readonly byte Blue;

public Color() {
Red = 0; Green = 0; Blue = 0;
}

public Color(byte red, byte green, byte blue)
{
Red = red; Green = green; Blue = blue;
}

public Color ShiftToGrayscale()
{
var avg = (byte)((Red + Green + Blue) / 3);
return new Color (avg, avg, avg);
}
}

Неизменяемость дает много преимуществ:
- Повышенная тестируемость
- Потокобезопасность.
- Атомарность неудачи.
- Отсутствие скрытых побочных эффектов.

Компилятор попытается помочь вам с неизменяемостью, но не сможет её гарантировать. Если в структуре есть список, его члены всё равно можно будет изменять:
public readonly struct MyStruct
{
public readonly List<int> myStuff
= new List<int>() { 1, 2 };

public MyStruct() {}

public void Mutate()
{
myStuff[0] = 3;
}
}

Окончание следует…

Источник:
https://steven-giesel.com/blogPost/2a52cd8d-b3b2-42e4-87e8-d6dc14147ddb
👍23
День 1716. #ЗаметкиНаПолях
Виды Структур в C#. Окончание
Начало

3. Ref-структура
Также в C# 7.2 Microsoft представили ref-структуру. ref-структуры гарантированно будут жить в стеке и никогда не смогут попасть в кучу. Благодаря этому вы можете реализовать сценарии с низкой аллокацией, в которых не возникает дополнительной нагрузки на сборщика мусора. Компилятор сделает это за вас, он запретит все сценарии, в которых тип значения перемещается в кучу.

Таким образом, ref-структура НЕ может:
- Быть типом элемента массива, полем класса или полем неref-структуры.
Поскольку массивы и классы всегда живут в куче, понятно, что размещать её там в качестве поля запрещено.
- Быть захвачена лямбда-выражением или локальной функцией.
Лямбда-выражение, а также локальные функции находятся за пределами родительской области видимости, и поэтому их необходимо упаковывать.
- Быть использована в асинхронном методе.
Компилятор создаёт конечный автомат для выполнения продолжения (кода после await), которая сама по себе является классом.
- Быть использована в итераторах.
Та же причина, компилятор создаёт конечный автомат, который продляет область действия/время жизни, и поэтому ref-структура окажется в куче.
- Реализовывать интерфейсы (кроме IDisposable).

Использование ref-структур очень ограничено, но всё же они имеют применение. Одним из наиболее ярких примеров является Span<T>.

4. Ref-структура только для чтения
Модификаторы ref и readonly можно объединить. Это будет означать, что структура должна быть неизменяемой и благодаря ключевому слову ref может находиться только в стеке.

5. Структуры-записи
Появились в C# 10. И подобно записям, которые «расширяют» классы, структуры-записи расширяют структуры. Это хороший синтаксический сахар от компилятора, который даёт вам «предопределённые» реализации Equals, ToString() и GetHashcode.
Мы можем легко переопределить нашу структуру Color:
public record struct Color(
byte Red, byte Green, byte Blue)
{
public Color ShiftToGrayscale()
{
var avg = (byte)((Red + Green + Blue) / 3);
return new Color (avg, avg, avg);
}
}

Все остальные методы компилятор сгенерирует за вас. Важно, что записи автоматически реализуют IEquatable<T>. Полную реализацию можно посмотреть на Sharplab.io.

6. Структуры-записи только для чтения
Конечно, вы также можете применить ключевое слово readonly к структуре-записи, оно будет делать то же самое, что и «обычная» структура, то есть компилятор будет налагать определённые ограничения для обеспечения неизменяемости.

7. Ref-структура-запись только для чтения
readonly ref record struct не существует))) Так же, как и ref record struct. Причина очень проста: структура записи реализует IEquatable<T>, а ранее мы видели, что ref-структурам не разрешено реализовывать какой-либо интерфейс, кроме IDisposable.

Источник: https://steven-giesel.com/blogPost/2a52cd8d-b3b2-42e4-87e8-d6dc14147ddb
👍18
День 1717. #ЧтоНовенького
C# Dev Kit для VS Code Теперь Общедоступен
Microsoft представили расширение C# Dev Kit для Visual Studio Code ещё в июне. Оно призвано обеспечить улучшенный опыт разработки на C# для Linux, macOS и Windows. В расширении используется технология с открытым исходным кодом, обеспечивающая гибкую и эффективную среду программирования для разработчиков .NET.

В прошлом месяце Microsoft объявили о прекращении поддержки Visual Studio для Mac. Это решение связано с тем, что они переключили своё внимание на оптимизацию Visual Studio и ориентацию VS Code для кроссплатформенной разработки. Поэтому на прошлой неделе Microsoft объявили, что C# Dev Kit станет общедоступным для всех. Хотя расширение общедоступно, важно отметить, что поддержка .NET MAUI и Unity всё ещё находится в предварительной версии.

Что касается будущего C# Dev Kit, в Microsoft намерены работать над ним на основе отзывов пользователей и постоянно улучшать его производительность, надёжность и набор функций для лучшей поддержки разработки C# в VS Code. Кроме того, пользователи могут ожидать ежемесячных обновлений расширения.

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

Объявление о выходе C# Dev Kit получило приличное количество комментариев сообщества Reddit. Например, пользователь SupaMook написал:
«Я пользователь Windows, какое-то время использовал VS2022, но сейчас практически полностью перешел на VSCode. Я думаю, что он прекрасно работает, и мне нравится, что я могу работать с одной IDE или, точнее, с редактором.
Единственное, что меня беспокоит, так это то, что работать на двух мониторах не так удобно. В отличие от VS, где вы можете просто перетащить файл на другой экран, в VSCode вам нужно заново открыть тот же workspace или что-то в этом роде (я не очень понял эту концепцию, но это единственный минус для меня)»

В комментариях также несколько раз обсуждалось лицензирование расширения, поэтому важно отметить, что C# Dev Kit согласует свой подход к лицензированию с Visual Studio. Это означает, что он бесплатен для частных лиц, академических пользователей и разработчиков открытого исходного кода на тех же условиях, что и версия Visual Studio для сообщества.

Для организаций C# Dev Kit поставляется в комплекте с подписками Visual Studio Professional и Enterprise, а также с GitHub Codespaces, что делает его доступным для широкого круга пользователей с различными потребностями.

Скотт Хансельман, вице-президент сообщества разработчиков Microsoft, опубликовал на YouTube очень познавательное видео. Там он продемонстрировал множество функций, таких как Solution Explorer, IntelliSense, отладка, лицензирование, а также общее описание расширения C# Dev Kit.

Также вчера прошёл стрим Visual Studio Toolbox о работе в C# Dev Kit.

Источник: https://www.infoq.com/news/2023/10/csharp-dev-kit-ga/
👍18👎1
День 1718. #юмор
👍32
День 1719. #ВопросыНаСобеседовании #Многопоточность
Самые часто задаваемые вопросы на собеседовании по C#

25. Как реализовать синхронизацию потоков с помощью ReaderWriterLockSlim? В чём его преимущества перед традиционным ReaderWriterLock?

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

Чтобы добиться синхронизации потоков с помощью ReaderWriterLockSlim, нужно:
1) Создать экземпляр ReaderWriterLockSlim.
2) Использовать EnterReadLock() перед доступом к общему ресурсу для чтения и ExitReadLock() после завершения операции чтения.
3) Использовать EnterWriteLock() перед доступом к общему ресурсу для записи и ExitWriteLock() после завершения операции записи.

var rwl = new ReaderWriterLockSlim();

// чтение
rwl.EnterReadLock();
try
{
// Доступ к общему ресурсу для чтения
}
finally
{
rwl.ExitReadLock();
}

// Запись
rwl.EnterWriteLock();
try
{
// Доступ к общему ресурсу для записи
}
finally
{
rwl.ExitWriteLock();
}

Преимущества ReaderWriterLockSlim над ReaderWriterLock:
1) Производительность: ReaderWriterLockSlim использует относительно дешёвую спин-блокировку и другие оптимизации для сценариев, в которых ожидается, что блокировка будет беспрепятственной или будет удерживаться в течение короткого времени.
2) Рекурсия: ReaderWriterLockSlim обеспечивает гибкую поддержку рекурсии блокировки, позволяя вам несколько раз входить и выходить из блокировки в одном потоке, тогда как ReaderWriterLock имеет ограничения на рекурсию.
3) Предотвращение нехватки писателей (writer starvation): ReaderWriterLockSlim имеет возможности уменьшить нехватку писателей, отдавая предпочтение запросам на блокировку записи вместо запросов на чтение. ReaderWriterLock может страдать от нехватки писателей при наличии непрерывного потока читателей.

Однако обратите внимание, что ReaderWriterLockSlim не поддерживает межпроцессную синхронизацию, и его не следует использовать, если объект блокировки необходимо использовать в нескольких процессах. В таких случаях используйте примитив синхронизации Mutex.

Источник: https://dev.to/bytehide/c-multithreading-interview-questions-and-answers-4opj
👍10
День 1720. #ЧтоНовенького
Доступна Бета-Версия Чата Github Copilot
Чат GitHub Copilot — это интерфейс чата, который позволяет разработчикам задавать вопросы о написании кода и получать ответы непосредственно в IDE. В настоящее время он находится в открытой бета-версии и доступен для всех индивидуальных пользователей GitHub Copilot в Visual Studio и VS Code.

Чат GitHub Copilot был представлен в виде общедоступной бета-версии как расширение для разработки ПО на базе искусственного интеллекта для всех пользователей GitHub Copilot для бизнеса. Теперь он доступен бесплатно для всех индивидуальных пользователей Copilot и поддерживается как в редакторах Visual Studio, так и в VS Code. Он использует расширенную обработку естественного языка (natural language processing, NLP), чтобы предлагать помощь и ответы на естественном языке непосредственно в поддерживаемых редакторах. Это избавляет разработчиков от необходимости обращаться к документации или искать на интернет-форумах. Уменьшая необходимость переключения контекста, он оптимизирует процесс разработки, что помогает разработчикам сохранять концентрацию и динамику.

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

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

Чтобы использовать чат Copilot, пользователи должны иметь активную подписку на GitHub Copilot, а пользователи VS Code должны иметь последнее обновление и войти в VS Code с тем же идентификатором GitHub, который имеет доступ к GitHub Copilot.

Источник: https://www.infoq.com/news/2023/10/github-copilot-chat-open-beta/
👍4👎2
День 1721. #Книги
«Код, который умещается в голове» (Симан М. — СПб.: Питер, 2023).

От создателя «Внедрения зависимостей на платформе .NET». Для тех, кто не осилил «Совершенный код»)))

На самом деле, очень интересная книга. Разработчикам с большим опытом она вряд ли откроет что-то новое, но поможет напомнить некоторые лучшие практики. Марк Симан проделал огромную работу, перелопатив более 100 источников по разработке, написанию кода, работе в команде и т.п., среди которых «Совершенный код», «Программист-прагматик», «Экстремальное программирование», «Чистый код», «Шаблоны корпоративных приложений», «Разработка через тестирование», «Непрерывное развёртывание ПО»… В общем, всё, что должен прочитать каждый уважающий себя разработчик, собрано в одном месте. Как организовать проект, как писать читабельный код без ошибок, как работать в команде, как искать и исправлять ошибки, как организовать рабочий процесс и многое другое. Везде по тексту есть ссылки на источники, так что в любую тему можно углубиться, прочитав источник.

Книга не без косяков, конечно, но претензий к автору и содержанию никаких. Вообще в книгах и статьях Симана придраться к чему-то очень сложно. Здесь все косяки исключительно на совести переводчиков. Например, по какой-то неведомой причине ВЕЗДЕ в примерах создания приложения для ресторана столики (tables) переведены как таблицы))) При чтении не сразу понимаешь о каком «резервировании таблиц» идёт речь, но потом просто мысленно заменяешь все таблицы на столы, и всё становится на свои места.

Короче, отнесу книгу к категории «мастрид». Особенно для начинающих свою карьеру в разработке (в .NET то точно, потому что в книге неплохо разобран пример создания .NET веб-API).
👍35
День 1722. #ЗаметкиНаПолях #Debugging
Правила Отладки: Ведите Журнал Аудита
Отладка - сложная задача, часто занимающая много времени. Поэтому наша цель - минимизация времени отладки. Каждая ошибка даёт возможность освоить методы предотвращения аналогичных проблем в будущем или быстрого их выявления, если они возникнут снова.

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

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

Ведение журнала аудита во время отладки даёт несколько преимуществ:
1) Комплексное отслеживание.
Журнал фиксирует действия по расследованию, позволяя вам перепроверять ваши действия и решения.

2) Эффективное сотрудничество.
Коллеги могут понять ваш мыслительный процесс, что поможет им быстрее решить проблемы и свести к минимуму дублирование усилий.

3) Точное воспроизведение.
Следуя описанным шагам, вы увеличиваете вероятность точного воспроизведения проблемы, что приводит к более эффективному устранению неполадок.

4) Диагностика коренных причин.
Анализ журнала помогает определить момент или последовательность событий, вызвавших ошибку, что важно для выявления первопричин проблемы и формулирования точных решений.

5) Целостное понимание.
Маршрут воспроизведения ошибки фиксирует не только действия, но и контекст и состояние системы. Это полезно для понимания более широкой картины проблемы.

6) Обучение и совершенствование.
Анализ прошлых записей журнала позволяет учиться на предыдущем опыте и может помочь избежать подобных ошибок в будущем.

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

8) Эффективная документация.
Журнал служит документацией усилий по отладке. Его можно использовать в дальнейшем для обучения новых членов команды и создания базы знаний.

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

10) Аналитика на основе данных.
Записывая наблюдения, гипотезы и результаты, вы собираете данные, которые могут дать представление о повторяющихся проблемах, закономерностях и потенциальных улучшениях процесса разработки.

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

Источник: https://dev.to/rajasegar/debugging-rules-keep-an-audit-trail-3he9
👍9
День 1723. #ЗаметкиНаПолях
Заменяем Разрывы Строк в C#
Замена разрывов строк в тексте - довольно распространённая задача. Иногда нужно сделать текст в одну строку, либо заменить разрыв строки HTML-элементом <br /> для отображения на веб-сайте. Посмотрим, как это можно сделать.

В C# используются два основных типа разрывов строк:
- \n: символ новой строки, используемый в системах Unix и Linux.
- \r\n: символ возврата каретки в сочетании с символом новой строки, используется в системах Windows.
Рекомендуемый способ записи разрыва строки — использовать Environment.Newline, который преобразуется в \n или \r\n в зависимости от системы.

1. Замена старым способом
Используем метод Replace() класса String:
const string text = 
"This is a line.\rThis is another line.";

var newText = text
.Replace("\r\n", "\n")
.Replace("\r", "\n");

Результат:
This is a line.\nThis is another line.

2. Новый способ
Метод ReplaceLineEndings(), представленный в .NET 6, является более специализированным инструментом для замены разрывов строк и это рекомендуемый подход. Требуется один аргумент: любая строка для строки замены (по умолчанию - Environment.NewLine)
var newText = text.ReplaceLineEndings("\n");

Результат будет таким же, как и в предыдущем способе.

3. Регулярные выражения
Аналогично методу Replace() класса String, можно использовать метод Regex.Replace():
var newText = Regex
.Replace(text, @"(\r\n|\r)", "\n");

И снова, результат будет тем же.

Производительность
Я протестировал производительность всех трёх способов в трёх версиях: .NET6, .NET7 и .NET8.
- string.Replace() примерно постоянен (~30-40нс).
- ReplaceLineEndings() примерно в 3 раза медленнее в .NET6 и .NET7 (~100-130нс), но в .NET8 он стал наоборот быстрее - ~25нс.
- Regex.Replace() примерно в 7 раз медленнее string.Replace() (~240-270нс) в .NET6 и .NET7, в .NET8 стал немного быстрее - ~200нс.

Однако, в .NET7+ для регулярных выражений мы можем использовать кодогенерацию (это даже предлагает сделать IDE):
var newText = MyRegex().Replace(text, "\n");

[GeneratedRegex(@"(\r\n|\r)")]
private static partial Regex MyRegex();
Тогда метод Regex станет быстрее: ~180нс в .NET7 и ~130нс в .NET8.

Памяти все методы используют одинаково – 96 байт.

Источник: https://code-maze.com/csharp-replace-line-breaks-in-a-string/
👍17
День 1724. #ЗаметкиНаПолях
Структурированный Параллелизм в C#
Структурированный параллелизм — это концепция, которая помогает писать более надёжный и удобный в обслуживании асинхронный код. Она не нова, но не так широко известна. Идея состоит в том, чтобы иметь возможность группировать асинхронные операции и гарантировать, что все операции завершатся до завершения группы. Это делается с помощью специальной конструкции, называемой областью задачи (Task Scope). Область задачи — это конструкция, похожая на блок try-catch-finally. Она используется для группировки асинхронных операций и обеспечения завершения всех операций до завершения группы.
Рассмотрим пример:
public class TaskScope
{
private CancellationTokenSource _cts = new();
private ConcurrentBag<Task> _tasks = new();

private TaskScope() { }

public static async Task Create(
Func<TaskScope, Task> act)
{
await using var ts = new TaskScope();
await act(ts);
await ts.WaitForAll();
}

public static async Task Create(
Action<TaskScope> act)
{
await using var ts = new TaskScope();
act(ts);
await ts.WaitForAll();
}

public async ValueTask DisposeAsync()
{
_cts.Cancel();
await WaitForAll();
}

public Task Run(
Func<CancellationToken, Task> act)
{
var task = Task.Run(async () =>
{
try
{
await act(_cts.Token);
}
catch (Exception ex) when (
ex is not OperationCanceledException)
{
_cts.Cancel();
throw;
}
});

_tasks.Add(task);
return task;
}

private async Task WaitForAll()
{
try
{
await Task.WhenAll(_tasks.ToArray());
}
catch (Exception ex) when (
ex is not OperationCanceledException)
{
throw;
}
}
}
Извините за много кода, но это только упрощённая реализация.
Идея в том, что мы определяем области в методе Create. Всё внутри Create будет выполняться в определённой области. Таким образом, задачи логически связаны друг с другом: если в одной выбрасывается исключение, мы отменяем все задачи вместе.
Использование:
var tasks = TaskScope.Create(group =>
{
group.Run(async token =>
{
await Task.Delay(100, token);
throw new Exception("Boom!!!");
});
group.Run(async token =>
await Task.Delay(1000, token));
});

// Выполняется 100мс
// после ошибки в 1й задаче отменяются все.
// Исключение поднимается наверх
try
{
await tasks;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}

// True
Console.WriteLine(tasks.IsFaulted);
Как видите, мы можем сгруппировать задачи и убедиться, что все они выполнены до того, как будет завершена область действия. Если одна из задач завершается сбоем, все остальные задачи отменяются и появляется исключение. Это очень мощная концепция, которая помогает писать более надёжный и удобный в обслуживании асинхронный код. Более сложную реализацию можно найти здесь.

Источник: https://steven-giesel.com/blogPost/59e57336-7c73-472f-a781-b0b79f0d47ad
👍25
День 1725. #Оффтоп #Обучение
Запертые Двери, Головные Боли и Интеллектуальные Потребности
Есть ли такие вещи, узнав о которых, вы начинали их видеть повсюду? Это «проблема очерёдности проблемы и решения». Сегодня поговорим о ней.

Игры
Новичок начинает игру с тщательно продуманного учебного уровня. Уровень приводит его к ключу, который он берёт, а затем к запертой двери, которую он успешно открывает. Далее в игре он натыкается на вторую запертую дверь… и оказывается в тупике. Он уже однажды решал эту проблему – почему ему так трудно решить её снова?

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

Математика
Помните, как на уроке математики вам давали концепцию, потом ещё более сложную концепцию, а вам всё это казалась совершенно бессмысленным? Проблема в том, что, прежде чем предлагать вам новую, более сложную концепцию, вам не дали возможности испытать ограничения старой. Другими словами: если вам представят решение (новую концепцию) до того, как представят тип задач, которые оно должно решить, решение, скорее всего, покажется бессмысленным и произвольным.

Представьте себя человеком, который продаёт аспирин. Лучший покупатель вашего аспирина — тот, у кого болит голова. Худшее, что вы можете сделать, — заставить людей, которые не чувствуют боли, принимать ваш аспирин. Они могут это сделать, но аспирин покажется им бессмысленным.

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

Монады — это решение конкретной проблемы: повторяющегося кода. Если вы пишете достаточно кода на функциональном языке, вы начинаете замечать, что пишете много подозрительно похожего кода для решения множества внешне разных задач. Было бы здорово, просто написать этот код один раз, а затем использовать его повторно, вместо того чтобы каждый раз переписывать его немного по-другому. То есть опытные функциональные программисты почувствовали головную боль, для которой монады являются аспирином. А новички ещё не заметили никаких повторяющихся закономерностей; или повторение их ещё не беспокоит. Головной боли просто нет.

Худшее, что вы можете сделать, — это заставить людей, которые не чувствуют боли, принимать аспирин. Точно так же попытка «обучить монадам» начинающих функциональных программистов, которые ещё не понимают необходимости в монадах, скорее всего, принесёт больше вреда, чем пользы, создавая дополнительную ненужную путаницу и увековечивая миф о том, что монады по своей сути сложны для понимания.

Итого
Строгая очерёдность проблем и решений является одним из определяющих фактором для эффективного понимания. Хорошо продуманный материал сразу же предлагает читателю поиграть с первоначальным расположением элементов и тем самым самостоятельно обнаружить потенциальные проблемы. Непосредственный опыт решения проблемы вызывает интеллектуальную потребность (или, если хотите, «головную боль») и создаёт основу для последующего внедрения решения. Поэтому лучшие обучающие материалы начинают с постановки проблемы, а не предлагают решение сразу. Но даже если это не так, изучая новую концепцию, придумайте (или погуглите), какую проблему она призвана решить. Это очень поможет пониманию и запоминанию.

Источник: https://mkremins.github.io/blog/doors-headaches-intellectual-need/
👍28
День 1726. #ЗаметкиНаПолях
Изменяем Имена Встроенных Ресурсов в .NET
Когда вы используете встроенные ресурсы (embedded resources) в проекте .NET, имя ресурса вычисляется на основе пути к файлу. По умолчанию используется формат, вроде <Имя сборки>.<Путь к файлу>. Но путь к файлу не содержит разделителя пути (/ или \). Вместо этого разделитель пути заменяется точкой. Например, для файла resources/index.html:
<Project>
<ItemGroup>
<EmbeddedResource Include="resources\index.html" />
</ItemGroup>
</project>

имя ресурса будет
MyProject.resources.index.html

Вы можете вывести список встроенных ресурсов, используя метод GetManifestResourceNames класса Assembly.
foreach(var name in
Assembly.GetExecutingAssembly()
.GetManifestResourceNames())
{
Console.WriteLine(name);
}

Пример вывода:
MyProject.resources.favicon.ico
MyProject.resources.img.background.png
MyProject.resources.index.html

Однако вы не можете быть на 100% уверены, как читать встроенные ресурсы, поскольку такое именование может вызывать конфликты.

Можно изменить имена встроенных ресурсов, используя LogicalName:
<Project>
<ItemGroup>
<EmbeddedResource Include="Resources/**/*">
<LogicalName>
$([System.String]::new('%(RelativeDir)').Replace('\','/'))%(FileName)%(Extension)
</LogicalName>
</EmbeddedResource>
</ItemGroup>
</Project>

Если добавить это в проект и снова выполнить код выше, получится что-то вроде этого:
Resources/favicon.ico
Resources/img/background.png
Resources/index.html

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

Источник: https://www.meziantou.net/customizing-the-embedded-resource-name-in-dotnet.htm
👍13
День 1727. #Testing
Тестирование на Основе Свойств. Теория
В этой серии рассмотрим, что такое тестирование на основе свойств, почему оно полезно и как оно может помочь писать лучший код.

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

Тестирование на основе свойств было введено в 2000 году Коэном Классеном и Джоном Хьюзом через библиотеку Haskell QuickCheck. Марк Симанн использует следующее определение в своем курсе Pluralsight по тестированию на основе свойств:
Тестирование на основе свойств — это метод автоматического тестирования, при котором вы постепенно концентрируетесь на правильном поведении системы, описывая её свойства или качества в общих чертах, а затем используете случайно сгенерированные тестовые данные для выполнения детерминированных тестов.

Оно довольно запутанное. Давайте разбираться.

Большинство тестов, которые пишут разработчики, — это тесты на основе примеров. Мы пытаемся подумать о том, какие входные данные будут репрезентативными для функций, которые мы хотим протестировать, и используем эти примеры для создания модульных тестов. Лучшие разработчики думают не только о счастливом пути, но и пытаются понять, что может быть неправильным вводом, чтобы увидеть, корректно ли функции обрабатывают условия сбоя.

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

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

Например:
int Add(int x, int y) => x+y;

Чтобы протестировать метод Add, мы можем попытаться придумать репрезентативные примеры, но существует бесконечный список возможных комбинаций. Вместо этого попробуем описать метод Add как связь между входными и выходными данными:
1) Не имеет значения, в каком порядке мы вводим входные данные x и y, выходные данные одинаковы. 1+7 = 7+1
2) Ноль вместо x или y равносилен пустой операции (вывод не меняется). 7+0 = 0+7 = 7.
3) Если вызывать Add несколько раз, порядок вызовов не имеет значения. 7+(4+5) = (7+4)+5

Это прекрасно согласуется с математическим описанием операции сложения.

Пока это всё теория. Завтра рассмотрим тесты на основе свойств в C#.

Продолжение следует…

Источник:
https://bartwullems.blogspot.com/2023/01/property-based-testing-in-cpart-1.html
👍14
День 1728. #Testing
Тестирование на Основе Свойств. Простой пример
Теория

Итак, напишем тесты согласно свойствам, которые мы определили.
int Add(int x, int y) => x+y;

У нас должно получиться 3 теста:
public class AddTwoNumbersTests
{
int Add(int x, int y) => x + y;

[Theory]
public void NoOrderOfParameters(int x, int y)
{
var res1 = Add(x, y);
var res2 = Add(y, x);

Assert.Equal(res1, res2);
}

[Theory]
public void ZeroDoesNothing(int x)
{
var res1 = Add(x, 0);
var res2 = x;

Assert.Equal(res1, res2);
}

[Theory]
public void OrderDoesntMatter(int x, int y, int z)
{
var res1 = Add(Add(x, y), z);
var res2 = Add(x, Add(y, z));

Assert.Equal(res1, res2);
}
}

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

FsCheck — это платформа тестирования на основе свойств, созданная для F#, но её можно использовать и в C#. Программист предоставляет спецификацию программы в виде свойств, которым должны удовлетворять функции, методы или объекты, а FsCheck проверяет, сохраняются ли эти свойства на большом количестве случайно сгенерированных случаев. Вы фактически пишете тестируемую спецификацию вашей программы. Спецификации выражаются на F#, C# или VB с использованием комбинаторов, определённых в библиотеке FsCheck. FsCheck предоставляет комбинаторы для определения свойств, наблюдения за распределением тестовых данных и определения генераторов тестовых данных. При сбое свойства FsCheck автоматически отображает минимальный контрпример.

Есть плагины FsCheck для всех популярных тестовых сред. Всё, что нам нужно, - это установить NuGet пакет и заменить атрибуты [Theory] на [Property].
Тесты по-прежнему будут проходить. По умолчанию FsCheck останавливается после 100 попыток. Если вы хотите знать протестированные значения, задайте свойству Verbose атрибута Property значение true:
[Property(Verbose = true)]

Вот пример вывода:
NoOrderOfParameters
Standard Output:
0:
(0, 0)
1:
(-1, 1)
2:
(1, 1)


Конечно, это очень простой пример. Далее рассмотрим что-то более реалистичное.

Продолжение следует…

Источник:
https://bartwullems.blogspot.com/2023/01/property-based-testing-in-cpart-2.html
👍13