.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
День 1381. #ЗаметкиНаПолях #Юмор
Творим Дичь в C#. Часть 2
В этой короткой серии постов рассмотрим разные странные и зачастую бесполезные вещи, которые можно делать в C#.

Ожидаем всё
Вы не устали писать конструкции вроде
await Task.Delay(10);

Очень много текста и всё равно единица измерения непонятна. Что если можно было бы сделать следующее:
// Подождем 2 секунды через TimeSpan
await TimeSpan.FromSeconds(2);

Да, вы правильно поняли, мы хотим дождаться объекта TimeSpan. И сделать это просто. Снова используем методы расширения. Всё, что мы говорили про foreach, применимо и к await. Он основан на шаблоне. Если у нас есть TaskAwaiter для определённого типа, мы можем ожидать этот тип. Есть хорошая статья от Microsoft, в которой Стивен Тауб рассматривает тему подробнее.

Как бы то ни было, нам нужно написать только один небольшой метод расширения, чтобы эта конструкция работала:
public static class Extensions
{
public static TaskAwaiter
GetAwaiter(this TimeSpan ts)
{
return Task.Delay(ts).GetAwaiter();
}
}

Вот и всё! У нас есть неявное преобразование из TimeSpan в TaskAwaiter, что позволяет нам ожидать TimeSpan. Но зачем останавливаться на этом? Мы можем пойти дальше в сторону fluent-интерфейсов и сделать что-нибудь вроде этого:
await 2.Seconds();

Красиво и выразительно. Единственное, что нам нужно сделать здесь, это добавить к имеющемуся выше методу, метод расширения для int, возвращающий TimeSpan:
public static class Extensions
{

public static TimeSpan Seconds(this int i)
=> TimeSpan.FromSeconds(i);
}

Таким образом, наш int становится неявным TimeSpan, который можно ожидать, потому что компилятор видит TaskAwaiter. Выглядит гораздо более читаемо, чем await Task.Delay.

Источник: https://steven-giesel.com/blogPost/5360d1c3-89f6-4a08-9ee3-6ddbe1b44236
👍23👎3
День 1382. #ЧтоНовенького
GitHub Codespaces Доступен Бесплатно для Всех Пользователей GitHub
Все пользователи GitHub могут бесплатно использовать размёщенные на GitHub среды разработки до 60 часов в месяц.

Codespace — это среда разработки, размещённая в облаке. Вы можете настроить свой проект для GitHub Codespaces, добавив файлы конфигурации (известные как контейнеры разработки) в свой репозиторий. Контейнеры разработки создают повторяемую конфигурацию среды разработки для всех пользователей вашего проекта.

Каждый кодспейс работает на виртуальной машине, размещённой на GitHub. Вы можете выбрать тип машины, которую хотите использовать, в зависимости от необходимых вам ресурсов. Доступны различные типы машин, начиная с 2-ядерного процессора, 4 ГБ ОЗУ и 32 ГБ памяти.

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

GitHub Codespaces теперь общедоступен для пользователей планов GitHub Free и GitHub Pro. Пользователям GitHub Free будет выделено до 60 часов бесплатного использования Codespaces в месяц, а пользователям плана GitHub Pro — 90 часов.

Кроме того добавлен ряд новых возможностей:
- Публично доступны в бета-версии IDE JetBrains, включая IntelliJ и PyCharm. Разработчики могут загрузить JetBrains Gateway и установить плагин GitHub Codespaces из JetBrains Marketplace. Редактор Visual Studio Code поддерживается в Codespaces со старта.
- Публично доступны в бета-версии JupyterLab Notebooks.
- Codespaces на GPU доступны в ограниченной бета-версии, предоставляя разработчикам возможность запуска моделей машинного обучения.
- Доступны шаблоны для запуска новых проектов на основе популярных платформ приложений. Правда, .NET шаблонов в списке нет.
- Для организаций добавлен REST API для управления и администрирования кодспейсов.

Источник: https://www.infoworld.com/article/3679948/github-codespaces-freely-available-to-all-github-users.html
👍7
День 1383. #Оффтоп
Сегодня порекомендую вам очередное видео с просторов ютуба.

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

В видео от канала Computerphile, доктор Джулиан Онионс рассказывает про протокол NTP (Network Time Protocol), с помощью которого компьютеры синхронизируют время между собой.

https://www.youtube.com/watch?v=BAo5C2qbLq8
👍5
День 1384. #ЗаметкиНаПолях #Microservices
Снижаем Проблемы от «Шумного Соседа» в Многопользовательском ПО
В многопользовательской системе множество клиентов совместно используют одни и те же вычислительные ресурсы. Это может привести к проблемам, если один клиент загрузит систему таким объёмом работы, что другие обнаружат снижение производительности. Иногда это называют проблемой «шумного соседа», по аналогии многоквартирным домом, где шумный сосед негативно влияет на жизнь других жильцов. Рассмотрим некоторые приёмы, позволяющие снизить ущерб от «шумного соседа».

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

2. API ограничений (Rate-limiting)
Мы можем отклонить некоторые вызовы для определённого клиента, разрешив при этом вызовы от других. Например, можно возвращать код ответа HTTP 429 Too Many Requests с заголовком Retry-After, сообщающим клиенту, когда можно будет повторить попытку.
Реализация ограничений для каждого клиента не всегда тривиальна. Нужно определить, что действительно один клиент монополизировал вычислительные ресурсы. Одним из подходов могут быть квоты, когда каждый клиент получает определённое количество разрешённых операций за период времени, прежде чем его вызовы попадут под ограничение.
.NET 7 включает промежуточное ПО ограничений (см. этот пост пункт 4 https://t.iss.one/NetDeveloperDiary/1487), значительно упрощающее эту задачу.
Недостатком этого подхода является то, что вы фактически лишаете клиентов возможности выполнять операции, что не очень удобно для них.

3. Приоритизация очередей
Альтернативой может быть приём запросов и использование очереди для их асинхронной обработки. Сообщения помещаются в очередь, а обработчик сообщений будет работать с отставанием, в итоге навёрстывая его, когда нагрузка спадёт. Но что, если один клиент добавит миллионы сообщений?
Один из вариантов решения — очередь для каждого клиента. Сообщения из разных очередей можно обрабатывать по очереди или параллельно, гарантируя, что каждый клиент получит равные шансы на обработку своего "первого" сообщения. Но если клиентов много, накладные расходы на одновременное обслуживание множества очередей сами по себе могут быть дорогостоящими и ресурсоёмкими.
Другой подход - «очереди с приоритетом». После определённого порога, каждое следующее сообщение от шумного клиента помещается в очередь с «низким приоритетом» и обрабатывается после обработки всех сообщений из основной очереди.

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

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

Источник: https://markheath.net/post/noisy-neighbour-multi-tenancy
👍7
День 1385. #ЗаметкиНаПолях #TipsAndTricks
Современные Приёмы в C#. Часть 1
Странно Повторяющийся Обобщённый Паттерн
Название предложено Стивеном Клири по аналогии со «Странно повторяющимся шаблоном» из C++.

Смысл паттерна в том, что интерфейс (или базовый тип) принимает в качестве параметра типа собственный производный тип:
interface IExample<TDerived> 
{ … }
class MyExample : IExample<MyExample>
{ … }

Если интерфейс (или базовый тип) хочет использовать полноценный производный тип в качестве параметра метода или возвращаемого значения, то он может сам определить эти методы, не возлагая никакой ответственности на производный тип. Вот пример из .NET BCL:
public interface IEquatable<T>
{
bool Equals(T? other);
}

Использование:
sealed class MyEquatable : 
IEquatable<MyEquatable>
{
public bool Equals(MyEquatable? other)
{ … }
}

MyEquatable.Equals реализует IEquatable<T>.Equals со строго типизированным аргументом MyEquatable. Без использования паттерна пришлось бы использовать IEquatable (принимая object) и терялась бы безопасность типов и эффективность.

Интерфейс (или базовый тип) также может использовать себя в качестве ограничения обобщения. Иногда это полезно, особенно для базовых типов:
abstract class MyBase<TDerived>
where TDerived : MyBase<TDerived>
{
// Методы могут использовать (TDerived)this
// Полезно при возврате TDerived
public virtual TDerived Get()
=> (TDerived)this;
}

class Derived : MyBase<Derived>
{
// Неявно имеет реализацию
// public Derived Get(), т.к. метод
// базового класса правильно определяет
// тип возвращаемого значения.
// Но может и переопределить его
}

Например, это полезно в Fluent API. В более общем случае ограничение обобщения необходимо в любой из этих ситуаций:
- Базовый тип должен рассматривать экземпляр MyBase<TDerived> (например, this) как производный тип (т. е. (TDerived)this). Это также может возникнуть при передаче this другим методам.
- Базовый тип должен обрабатывать TDerived как MyBase<TDerived>, например, вызывая приватные базовые методы для экземпляра типа TDerived, отличного от this. В этом случае явное приведение не требуется.

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

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

Источник: https://blog.stephencleary.com/2022/09/modern-csharp-techniques-1-curiously-recurring-generic-pattern.html
👍14
День 1386. #ЧтоНовенького
Утилита для Создания Токенов JWT на Время Разработки
Допустим, вы создаёте защищённый API. Для аутентификации/авторизации используются JWT. Нужно убедиться, что API отклоняет неправильные или поддельные токены и разрешает доступ с правильными. Можно написать собственный код для генерации токенов JWT или можно зарегистрировать клиентов с нужными параметрами в Identity Server, если он у нас есть.

Однако в .NET 7 появился инструмент dotnet user-jwts. Он помогает создавать и управлять JWT во время разработки для проекта, который мы создаём.

Создадим простой API:
using System.Security.Claims;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthorization();
builder.Services
.AddAuthentication("Bearer")
.AddJwtBearer();

var app = builder.Build();

app.MapGet("/", () => "Hello, World!");
app.MapGet("/secret",
(ClaimsPrincipal user) =>
$"Hello {user.Identity?.Name}!")
.RequireAuthorization();

app.Run();

В нашем API 2 конечные точки:
- открытая в корне, которая выдаёт строку "Hello, World!";
- закрытая /secret, выдающая приветствие авторизованному пользователю.

Теперь добавим возможность использовать локальные JWT токены.
dotnet user-jwts create

Эта команда создаст токен для текущего пользователя компьютера, а также добавит в файл appsettings.Development.json блок "Authentication" с необходимыми настройками для использования Bearer-аутентификации.

Мы можем задавать различные параметры для JWT токена, например, имя пользователя, список клеймов и разрешений (полная документация команды):
dotnet user-jwts create --name MyTestUser --scope "myapi:secrets"

Эта команда создаст пользователя с именем MytestUser и разрешением для "myapi:secrets". Вывод будет примерно такой:
New JWT saved with ID 'd0f7fe96'.
Name: MyTestUser
Scopes: myapi:secrets

Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVC…

Вот и всё. Теперь мы можем использовать любой REST-клиент, вроде Postman, для доступа к закрытой конечной точке. Скопируйте значение Token, выданное утилитой user-jwts. В Postman создайте Get запрос к https://localhost:7107/secret. На вкладке Authorization выберите Type: Bearer Token и вставьте значение токена в поле. Выполните запрос. Он должен выдать код ответа HTTP 200 и ответ:
Hello MyTestUser!

Источник: https://learn.microsoft.com/en-us/aspnet/core/security/authentication/jwt-authn
👍23
День 1387. #ЗадачиНаСобеседовании
Давненько у нас не было задачек. Сегодня предложу вам подумать над проектированием решения в контексте базы данных. Единственно правильного ответа тут, наверное, нет, просто предлагайте варианты.

Допустим, у нас есть огромная таблица инвентаря товаров в наличии в разных магазинах сети. Т.е. в таблице будет id магазина, артикул товара, количество и прочая информация о товаре. Из неё нас интересуют поля ID магазина и артикул. Таблица несколько раз в день обновляется по мере того, как магазины получают или распродают свой товар и присылают нам остатки.

Задача в следующем. Для клиента сайта требуется организовать «живые» подсказки в строке поиска. То есть по мере того, как клиент печатает артикул (для простоты пусть будет поиск только по артикулу), подсказка под полем показывает доступные артикулы и количество магазинов, в которых они есть в наличии. Например:

Поиск: ABC

Подсказка:
ABC280    - 10 магазинов
ABC100 – 5 магазинов
ABC101 – 2 магазина
ABCDEF-92 – 1 магазин
ABC-XYZ – 1 магазин

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

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

Фронтенд нас сейчас не интересует. Интересует быстрое получение результата на бэкенде. Как бы вы с точки зрения хранения данных и получения их из базы реализовали такой функционал?
👍2
День 1388. #Карьера
6 Принципов Разработки Проектов с Открытым Кодом
Работа над проектом с открытым кодом, будь то в качестве автора, сопровождающего или разработчика, может быть довольно сложной! Сегодня посмотрим, на какие важные уроки можно извлечь при разработке open-source проектов.

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

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

3. Это отличная возможность для обучения
Независимо от того, будет ли ваш пакет скачан миллионы или всего пару раз, его создание — отличная возможность узнать больше об открытом коде, модульном тестировании, дизайне API и т.д., чем в повседневной работе.
Существует также огромная возможность обучения от взаимодействия с сообществом. Именно они сообщат вам, какой дизайн им нравится, а какой нет. Иногда одна и та же проблема возникает несколько раз в течение нескольких месяцев. Это указывает на то, что нужно переосмыслить дизайн, поскольку он оказался не таким удобным для пользователей, как вы ожидали.
Кроме того, это прекрасная возможность сотрудничества с другими разработчиками.

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

5. Звёзды Github - не гарантия качества
Знание того, как часто используется пакет, очень помогает понять, насколько он популярен. Тем не менее, многие по-прежнему используют звёзды на Github, чтобы оценивать качество и популярность.
Однако большое количество звёзд может быть просто результатом правильного маркетинга. Звезда от пользователя даже не означает, что он использовал это ПО или что оно действительно работает. Количество загрузок пакета гораздо лучший показатель. Кроме того, можно посмотреть на количество форков или активность обсуждения.

6. Психология разработки API
Убедитесь, что пакет прост в использовании и установке, что значительно упрощает его внедрение. Когда вы сильно сосредотачиваетесь на дизайне: модульности, слабой связанности и следовании лучшим практикам, - некоторые пакеты становятся просто взрывом мозга при использовании.
Посмотрите на разработку новых функций с точки зрения психологии, и это значительно облегчит понимание того, на чём следует сосредоточиться. Что нужно пользователям? Почему пользователи на самом деле используют этот пакет? Каковы основные недостатки вашего кода?
Потратьте время на то, чтобы понять, как облегчить внедрение вашего пакета для среднестатистического пользователя. Всё это часто сводится к одному правилу: сделайте это супер простым!
Чем сложнее установить и использовать пакет, тем с меньшей вероятностью пользователи будут использовать его в своём рабочем процессе.

Источник: https://towardsdatascience.com/6-lessons-i-learned-from-developing-open-source-projects-4617e26f247c
Автор оригинала: Maarten Grootendorst
👍6
День 1389. #Оффтоп #Карьера
Сегодня порекомендую вам видео на отвлечённую тему. «4 вещи, необходимые, чтобы стать экспертом» (The 4 things it takes to be an expert). https://youtu.be/5eW6Eagr9XA

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

Автор приводит данные различных исследований и несколько интересных историй.

Извините, видео на английском, но можно включить автоматически переведённые субтитры.
👍6
День 1390. #ЗаметкиНаПолях #AsyncTips
Блокировки с async

Задача
Имеются общие данные. Требуется обеспечить безопасное чтение и запись этих данных из разных блоков кода, внутри которых может использоваться await.

Решение
Тип SemaphoreSlim был обновлён в .NET 4.5 для обеспечения совместимости с async. Пример использования:
class MyClass
{
private int _value;
// Блокировка защищает поле _value
private readonly SemaphoreSlim
semaphore = new SemaphoreSlim(1);

public async Task IncAsync(string name)
{
await semaphore.WaitAsync();
try
{
int oldValue = _value;
await Task.Delay(1000 * oldValue);
_value = oldValue + 1;

Console.WriteLine($"{name}: {_value}");
}
finally
{
semaphore.Release();
}
}
}

Тестовый вариант использования класса:
var cls = new MyClass();

for (int i = 1; i <= 5; i++)
{
int count = i;
Thread t = new(
async () =>
await cls.IncAsync("Поток " + count)
);
t.Start();
}
Console.ReadLine();

Этот код будет выводить одну за другой строки с постоянно увеличивающейся задержкой между ними:
Поток 4: 1
Поток 3: 2
Поток 2: 3
Поток 5: 4
Поток 1: 5

Заметьте, что SemaphoreSlim принимает в качестве параметра конструктора количество потоков, которые одновременно могут получить доступ к блокировке. В данном случае мы задали 1.

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

Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 12.
👍23
День 1391. #ЗаметкиНаПолях
Подробнее об Обязательных Членах Класса в C#11
В C#11 представлено новое ключевое слово required, которое может применяться к полю и свойству класса, записи или структуры.
class Foo {
internal required int _field;
internal required int
Prop { get; set; }
}

Оно обязывает вызывающий код инициализировать поле или свойство. Если этого не сделать:
var foo = new Foo();
мы получим ошибку CS9035: Required member 'Foo._field' must be set in the object initializer or attribute constructor. (Обязательный член 'Foo._field' должен быть задан в инициализаторе объекта или конструктор должен иметь атрибут.)

Это очень полезное дополнение для улучшения сценария инициализации объекта, особенно в отношении init-свойств, которые могут быть инициализированы только в конструкторе или в инициализаторе.

Ограничения обязательных членов
1. required не может применяться к закрытому члену, поскольку ограничение ключевого слова действует за пределами родительского класса, когда вызывающий объект создаёт его экземпляр.
2. required не может применяться к статическому члену, поскольку оно предназначено для использования только во время создания экземпляра объекта.
3. required не может применяться к члену, доступному только для чтения, который может быть назначен только в конструкторе.

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

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

Обязательные члены и переопределение свойств
Есть несколько ограничений при переопределении обязательных свойств:
1. required нельзя использовать интерфейсах и для членов, являющихся явной реализацией интерфейса.
2. При переопределении обязательного виртуального или абстрактного свойства ключевое слово required должно быть указано повторно.
interface IInterface
{
int Prop { get; init; }
// ОШИБКА
required int Foo { get; init; }
}

abstract class Base
{
public required abstract int
Abstr { get; init; }
public required virtual int
Virt { get; init; }
}

class Derived : Base, IInterface
{
public int Foo { get; init; }
// ОШИБКА
required int IInterface.Prop { get; init; }

// required повторно в переопределениях
public required override int
Abstr { get; init; }
public required override int
Virt { get; init; }
}

Источник: https://blog.ndepend.com/c-11-required-members/
👍9
День 1392. #TipsAndTricks
Советы и Инструменты Отладки. Начало
В этой серии постов приведу несколько советов и инструментов, которые помогут отлаживать приложения .NET. Список не исчерпывающий, здесь только некоторые варианты.

Коротко случайные советы
- Поговорите с резиновой уточкой.
- Прогуляйтесь, поспите, поиграйте в игру и т.д., а потом вернитесь к своему коду на свежую голову.
- Внимательно прочитайте документацию.
- Прежде чем изменять код, убедитесь, что вы понимаете, почему он ведёт себя не так, как вы ожидаете.
- Напишите тест, который воспроизводит ошибку, чтобы вы могли быстро исправить её.
- Удалите/закомментируйте весь код, который не нужен для воспроизведения ошибки. В итоге у вас должен получиться небольшой фрагмент кода, воспроизводящий ошибку (используйте систему контроля версий, чтобы вы всегда могли вернуться к исходному коду).
- Можно сделать наоборот: создать новое приложение и постепенно добавлять код, пока ошибка не появится.
- Время, потраченное на использование отладчика, едва ли ускорит решение последующих задач. А вот улучшение логирования и сообщений об ошибках поможет. Это может сэкономить вам или вашим коллегам время в будущем.

А теперь более подробные советы.

1. Код выполняется?
Если нет уверенности, что код действительно выполняется, добавьте throw или Console.Write в код. Да, звучит банально, но отладочный вывод в некоторых случаях сделать проще всего. Для надёжности можно обернуть этот код в директивы препроцессора:
public void MethodToDebug()
{
#if DEBUG
Console.WriteLine("Debugging");
throw new Exception("Debugging");
#endif
//…
}

2. Запуск отладчика из кода
Если вы не можете запустить процесс в режиме отладки и не можете найти способ присоединить отладчик к существующему процессу, вы можете использовать System.Diagnostics.Debugger.Launch() или System.Diagnostics.Debugger.Break(); там, где вы хотите установить точку останова. См. подробнее об их использовании.

В Windows отладку процесса можно начать из Диспетчера Задач. Нажмите правой кнопкой мыши на нужный процесс и выберите Debug (Отладка).

3. Отладка более чем одного процесса за раз
Visual Studio может отлаживать несколько процессов одновременно. Например, вы можете одновременно отлаживать веб-приложение и консольное приложение. Для этого перейдите в Debug > Set Startup Projects… (Отладка > Установить стартовые проекты) и установите на запуск несколько проектов. Кроме того, в меню Debug > Attach to Process… (Отладка > Прикрепить к процессу…) можно выбрать несколько процессов.

4. Наблюдение за значением переменной без отладчика
Если вы не можете подключить отладчик, вы можете регистрировать значения с помощью Console.WriteLine(), File.WriteAllText() или Logger.LogDebug() для вывода значений, которые вы хотите наблюдать.

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

Источник:
https://www.meziantou.net/debugging-tips-and-tools.htm
👍13
День 1393. #TipsAndTricks
Советы и Инструменты Отладки. Продолжение
Начало

5. Загрузка символов сборки
Для отладки скомпилированных сборок необходимо загрузить файлы символов, сопоставляющие бинарный код со строками кода в текстовых файлах. Последние версии Visual Studio загружают символы автоматически. В более старых версиях можно пройти в Debug > Windows > Modules (Отладка > Окна > Модули), там найти модуль, который хотите отладить, и нажать Load Symbols (Загрузить символы).
Если символы найти не удаётся, возможно нужно включить (или добавить) символьные серверы. В настройках (Tools > Options…) зайдите в Debugging > Symbols (Отладка > Символы).

6. Различные виды точек останова
Есть много способов остановить выполнение кода:
- Точки останова: остановка всегда при выполнении выражения.
- Условные точки останова: остановка при выполнении выражения, когда соблюдается условие.
- Точки останова в данных: остановка, когда значение данных в памяти меняется
- Зависимые точки останова: остановка, только когда другая точка останова достигнута. Полезны в сложном сценарии, таком как отладка многопоточного приложения.
- Остановка при исключении

7. Добавление утверждений
Точки останова полезны для отладки кода, но их не всегда легко использовать. Вы можете использовать утверждения для проверки состояния вашего кода.
public void MethodToDebug()
{
for (int i = 0; i < 100; i++)
{
Debug.Assert(i != 50, "i не должно быть 50");
Console.WriteLine(i);
}
}
Замечание: Debug.Assert доступно только при сборке в режиме отладки.

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

Источник:
https://www.meziantou.net/debugging-tips-and-tools.htm
👍10
День 1394. #TipsAndTricks
Советы и Инструменты Отладки. Продолжение
Начало 1-4
Продолжение 5-7

8. Наблюдение за приложением без отладчика
Вы можете наблюдать за http-вызовами, используя прокси-сервер, такой как Fiddler.

В браузерах на движке Chromium помимо вкладки Network (Сеть) в инструментах разработчика, есть:
- about://net-internals/ - различные сетевые инструменты,
- about://net-export/ - экспорт данных по сетевым соединениям.

Также следить за сетевым трафиком можно с помощью WireShark.

Вы можете следить за файловой системой, регистром и активностью процессов/потоков с помощью procmon (Монитор Процессов) или wtrace.

Можно использовать dotnet-counter для быстрого мониторинга таких вещей, как загрузка ЦП или частота исключений, возникающих в вашем приложении .NET Core.

dotnet-trace или PerfView используются для сбора ETW-событий вашего приложения .NET Core. Это позволяет получить много информации о приложении, такой как запросы к базе данных, HTTP-вызовы, DNS-запросы, конфликты блокировок, информация о пуле потоков, сборках мусора, JIT, загрузке сборок и т.д.

Анализатор производительности Windows создаёт графики и таблицы данных для событий трассировки событий Windows (ETW).

Дамп приложения можно сделать с помощью dotnet-dump и проанализировать его позже с помощью Visual Studio. Если вам нужно создать дамп при выполнении определенных условий, вы можете попробовать ProcDump.

Handle или ProcessExplorer помогут перечислить дескрипторы (открытые файлы, ключи реестра, порты, примитивы синхронизации).

Visual Studio предоставляет множество инструментов для отладки приложения. Кроме того, вы можете использовать WinDbg и dotnet-sos. Инструменты Sysinternals также очень полезны.

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

Источник:
https://www.meziantou.net/debugging-tips-and-tools.htm
👍9
День 1395. #TipsAndTricks
Советы и Инструменты Отладки. Окончание
Начало 1-4
Продолжение 5-7
Продолжение 8

9. Просмотр значений в отладчике
Окна Watch – Debug > Windows > Watch > Watch 1 (Отладка > Окна > Просмотр > Просмотр 1) позволяют оценивать несколько выражений. Все выражения пересчитываются, когда выполнение приостанавливается. Обратите внимание, что вы можете использовать такие команды, как nq (без кавычек) или nse (без побочных эффектов), h (шестнадцатеричный). См. подробнее про форматирование выражений в отладчике.

Идентификатор объекта (Object ID) позволяет создать глобально доступный идентификатор для объекта. Таким образом, даже если значение недоступно в текущем контексте, вы всё равно можете сослаться на значение, используя $N, где N — идентификатор объекта. См. подробнее.

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

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

Окно Immediate (Debug > Windows > Immediate при отладке) позволяет в точке останова выполнять произвольный код и видеть результат.

10. Отладка многопоточных приложений
- Получение списка запущенных потоков (Debug > Windows > Threads). Вы можете дважды щелкнуть по потоку, чтобы переключиться на него.

- Получение списка задач в .NET (Debug > Windows > Tasks).

- Окно Parallel watch (Debug > Windows > Parallel Watch > Parallel Watch 1) позволяет оценить значение объекта во всех потоках.

- Окно Parallel Stacks объединяет все похожие стеки/задачи вместе и показывает количество потоков/задач, выполняющих один и тот же код. Это даст вам хорошее представление о том, что происходит в вашем приложении.
Если вы отлаживаете несколько приложений, окно «Parallel Stacks» может объединять стеки вызовов из всех запущенных процессов.

11. Другие инструменты
- Проверка используемых портов в Windows
PowerShell
$Port = 5000
Get-Process -Id (Get-NetTCPConnection -LocalPort $port).OwningProcess
Get-Process -Id (Get-NetUDPEndpoint -LocalPort $port).OwningProcess

netstat -a -b

- Проверка конфигурации DNS
PowerShell
Resolve-DnsName -Name www.google.com
Resolve-DnsName -Type A -Name www.google.com

Также можно использовать онлайн сервис, вроде https://dnslookup.online/.

Источник: https://www.meziantou.net/debugging-tips-and-tools.htm
👍6
День 1396. #ЧтоНовенького
Dev Tunnels в Превью для Проектов
ASP.NET Core
Туннели разработки (dev tunnels), переадресация портов, или туннелирование портов (Microsoft использует все три термина) обеспечивают специальные соединения между машинами, которые не могут напрямую подключаться друг к другу.
Проще говоря, это позволяет удаленным компьютерам в общедоступной среде подключаться к устройствам или службам в частных сетях или наоборот.

В Visual Studio 17.4 вы можете включить функцию туннелей разработки в Tools> Options > Environment > Preview Features (Инструменты > Параметры > Среда > Предварительный Просмотр Функций). Кроме того, потребуется войти в аккаунт в Visual Studio.

Также в файл launchSettings.json нужно добавить два свойства:
devTunnelEnabled — при значении true туннель будет использоваться при запуске проекта.
devTunnelAccess — для портов, используемых этим конкретным проектом:
- private (по умолчанию) — только для авторизованных пользователей,
- org — только для пользователей организации, в которой был создан туннель,
- public – разрешён анонимный доступ.
{
"iisSettings": {

},
"profiles": {
"TemplatesWeb": {
…,
"applicationUrl": "https://localhost:44318",
"devTunnelEnabled": true,
"devTunnelAccess": "public"
}
}
}

Теперь после локального запуска вашего проекта, с ним будет связан общедоступный URL-адрес. Его можно использовать для доступа к проекту из вне вашего локального компьютера, например, при разработке веб-перехватчиков, приложения Power Platform, которое вызывает веб-API ASP.NET Core, или совместного использования незавершённой работы. На данный момент при каждом запуске URL будет меняться, но это обещают исправить в будущем.

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

Поддерживаемые типы учетных записей
Поддерживаются аккаунты Azure, Microsoft (MSA), а также GitHub. Поддержки организаций для учетных записей GitHub в настоящее время нет, но планируется.
В меню Tools > Options > Dev Tunnels (Инструменты > Параметры > Туннели разработки) можно настроить учётную запись, которая используется при создании и использовании туннелей разработки.

Дальнейшее развитие
Текущая модель поддержки туннелей разработки требует, чтобы пользователи записывали значения в файл launchSettings.json. В будущем появится пользовательский интерфейс в Visual Studio, который позволит более явно создавать туннели и управлять ими. Помимо этого обещают:
- Возможность настроить туннель под использование постоянного или временного URL.
- Создание туннелей на разных аккаунтах.
- Управление настройками туннеля.
- UI для получения токена доступа к туннелю.
и другие улучшения.

Источник: https://devblogs.microsoft.com/visualstudio/public-preview-of-dev-tunnels-in-visual-studio-for-asp-net-core-projects/
👍15
День 1397. #БазыДанных
Сегодня порекомендую видео про нормализацию базы данных. 1я, 2я, 3я, 4я и даже 5я нормальные формы. Автор на простых примерах объясняет, что это такое, почему это важно и как позволяет избежать несогласованности данных.

Приятного изучения https://youtu.be/GFQaEYEc8_8

Извините, видео на английском, но можно включить автоматически переведённые субтитры.
👍10
День 1398. #ЗаметкиНаПолях #AsyncTips
Блокирующие и Асинхронные Сигналы

1. Блокирующие Сигналы
Задача:
требуется отправить уведомление от одного потока другому.

Решение
Самый распространённый и универсальный межпотоковый сигнал — событие с ручным сбросом ManualResetEventSlim. Оно может находиться в одном из двух состояний: установленном или сброшенном. Любой поток может перевести событие в установленное состояние или сбросить его. Поток также может ожидать перехода события в установленное состояние.
Следующие два метода вызываются разными потоками; один поток, использует Wait() и ожидает сигнала от другого, который вызывает Init():
class MyClass
{
private readonly ManualResetEventSlim
_mres = new();
private int _val;

public int Wait()
{
_mres.Wait();
return _val;
}
public void Init()
{
_val = 42;
_mres.Set();
}
}

ManualResetEventSlim является синхронным сигналом, поэтому WaitForInitialization блокирует вызывающий поток до отправки сигнала.

2. Асинхронные Сигналы
Задача:
требуется отправить уведомление от одного потока другому, при этом получатель оповещения должен ожидать его асинхронно.

Решение
Если уведомление должно быть отправлено только один раз, можно использовать TaskCompletionSource<T>. Код-отправитель вызывает TrySetResult, а код-получатель ожидает его свойство Task:
class MyClassAsync
{
private readonly TaskCompletionSource<object?>
_set = new();
private int _val1;
private int _val2;

public async Task<int> WaitAsync()
{
await _set.Task;
return _val1 + _val2;
}
public void Init()
{
_val1 = 42;
_val2 = 69;
_set.TrySetResult(null);
}
}

TaskCompletionSource<T> может использоваться для асинхронного ожидания любой ситуации — в данном случае уведомления от другой части кода. Этот способ хорошо работает, если сигнал отправляется только один раз, но не работает, если сигнал нужно не только включать, но и отключать.

Итого
Сигналы представляют собой механизм уведомлений общего назначения, однако использовать их следует только тогда, когда это действительно уместно. Если «сигнал» представляет собой сообщение, отправляющее некоторые данные между потоками, рассмотрите возможность использования очереди «производитель/потребитель». А если сигналы используются только для координации доступа к общим данным, лучше использовать lock или асинхронную блокировку.

Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 12.
👍13
День 1399. #ЗаметкиНаПолях #TipsAndTricks
Современные Приёмы в C#. Часть 2
Часть 1

Записи-Значения
Записи-значения представляют собой форму более общего паттерна «объект-значение», специфичную для последних версий C#.

Антипаттерн, с которым мы здесь боремся, называется Одержимость Примитивами (Primitive Obsession). Разработчик чрезмерно использует примитивы (string, int, Guid, decimal и т.д.) для представления концепций бизнеса или предметной области. Одним из классических примеров являются идентификаторы сущностей. Здесь есть 2 проблемы:
1. Примитивы не являются типобезопасными. Легко случайно передать идентификатор клиента методу, ожидающему идентификатор ресурса. Или, если методу нужны и customerId, и resourceId, можно легко передать параметры в неправильном порядке.
2. Примитивы поддерживают операции, которые не имеют смысла. Например, если customerId имеет тип int, компилятор с радостью позволит вам разделить его на 2, но это не имеет смысла с точки зрения бизнеса.

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

Записи-значения
Современный способ создать объект-значение в C# - это записи-значения:
public readonly record struct CustomerId(string Value);

По порядку:
- Записи (record) обеспечивают семантику значений, дополненную равенством по значению и поддержкой ToString.
- Структуры-записи (record struct) предоставляют оболочку типа значения, избегая выделения в куче (размер оболочки равен размеру обёрнутого значения).
- Структуры-записи только для чтения обеспечивают неизменность.

Поскольку определение типа представляет собой всего одну строку, все типы записей-значений можно собрать в одном файле, вроде Primitives.cs.

Советы
1. Избегайте публичности
Записи-значения лучше всего работают как внутренние типы. Не в смысле internal, а в смысле в ядре вашего приложения. Не пытайтесь настроить сериализацию записей-значений. Путь ваш код просто оборачивает примитивные типы, полученные извне:
CustomerId customerId = new(customerIdIntValue);
и наоборот, разворачивает их обратно при отправке информации:
int customerIdIntValue = customerId.Value;

2. Выбор примитивов для замены
Не все примитивы должны быть записями-значениями. Рекомендуется использовать примитивы по умолчанию, а записи-значения в следующих ситуациях:
- Есть похожие типы значений (концептуально похожие или просто имеющие похожее имя), которые используются вместе. Например, идентификаторы сущностей, имеющие похожие имена, и когда есть несколько методов, которые одновременно работают с разными типами идентификаторов.
- Нужно гарантировать, что критически важный тип-значения должен использоваться только определённым образом. Например, компонент, отправляющий e-mail с персональной информацией должен использовать только подтверждённые адреса. Тогда он может принимать тип ValidatedEmail. А свойство этого типа задаётся у пользователя только при подтверждении e-mail.

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

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

Источник: https://blog.stephencleary.com/2022/10/modern-csharp-techniques-2-value-records.html
👍18
День 1400.
35 Заблуждений о Дате и Времени. Начало
Дата и время – наверняка «любимый» тип данных очень многих программистов. В этой серии постов рассмотрим заблуждения о дате и времени. Просто факты, без объяснений, которые возможно и не пригодятся вам в повседневной работе, но всё-таки, думаю, что будет полезно иметь в виду.

1. Все используют один и тот же календарь
Не все страны перешли на григорианский календарь одновременно. Франция, Испания, Италия и некоторые другие страны перешли 15 октября 1582 года. Британская империя - в сентябре 1752. Россия не переходила до 1917, Греция - до 1923, а Садовская Аравия и вовсе перешла только в 2016 году.
В некоторых культурах используются разные календари, основанные на лунных, а не на солнечных циклах, или на комбинации лунных и солнечных.

2. 1 год = 12 месяцев
В некоторых календарях используются високосные месяцы, поэтому в году может быть 13 месяцев. В .NET BCL DateTimeFormat.GetMonthName принимает значение от 1 до 13 для поддержки календарных систем с високосными месяцами, например реализованных классами JewishCalendar и EastAsianLunisolarCalendar. В еврейском календаре Адар является 6-м месяцем обычного года, который становятся Адар 1 и Адар 2 (месяцы 6 и 7) в еврейском високосном году.

3. В году 365 дней
В високосном году 366 дней.

4. В году 365 или 366 дней
В прошлом были различные примеры, когда некоторые страны меняли свой календарь. В этом случае год может быть короче. Например, Великобритания изменила свой календарь в 1752 году, и в сентябре не хватает нескольких дней:
Su Mo Tu We Th Fr Sa
1 2 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30

Другим примером является Самоа, где изменили часовой пояс в 2011. В этом году пятницы, 30 декабря, не существует. Поскольку 2011 год не високосный, в том году было всего 364 дня.
TimeZoneInfo
.FindSystemTimeZoneById("Pacific/Apia")
.IsInvalidTime(new DateTime(2011, 12, 30)));
// True

Еврейский календарь является лунным календарем. Лунные месяцы короче солнечных. Таким образом, каждые несколько лет они добавляют дополнительный месяц. Таким образом, вы получаете год короче 365 дней, а затем год с дополнительным месяцем в 380 с чем-то дней.

5. Дни идут подряд
Если вы посмотрите на календарь из п.4 то увидите, что это не так. После 2го сентября идёт 14е.

6. За пятницей всегда следует суббота
Опять же, в календаре из п.4 дни идут не подряд. За пятницей 16 идёт воскресенье 17.

7. Високосные года идут каждые 4 года
Год должен делиться на 4 без остатка. Если год также можно без остатка разделить на 100, это не високосный год. Если только год не делится на 400 без остатка. Тогда это високосный год:
1900 год не високосный
2000 год високосный
2020 год високосный

8. Кто угодно может правильно реализовать логику високосных годов
Excel ошибочно предполагает, что 1900 год является високосным, и причина этой ошибки хорошо объяснена в документации.

9. GMT совпадает с UTC
Всемирное координированное время (UTC) — это международная шкала времени, рекомендованная Международным бюро мер и весов (BIPM) в качестве базисного времени.
Среднее время по Гринвичу (GMT) — это часовой пояс, который является средним солнечным временем в Королевской обсерватории в Гринвиче, Лондон. Это соответствует UT1, солнечному времени на 0° долготы.
Разница во времени между UTC и GMT составляет доли секунды. Поэтому для общих целей оба времени считаются одинаковыми. Но эта разница важна для научных целей.

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

Источник:
https://www.meziantou.net/misconceptions-about-date-and-time.htm
👍19
День 1401.
35 Заблуждений о Дате и Времени. Продолжение
Начало

10. Смещение часового пояса всегда представляет собой целое число часов
Для многих часовых поясов нет. Летнее время Ньюфаундленда — UTC-2:30. Центрально-западное стандартное время Австралии — UTC+8:45.

11. Смещение часового пояса всегда представляет собой целое число минут
В прошлом было много контрпримеров, где смещение содержит секунды:
Зона Азия/Кабул 4:36:48 LMT 1890
Зона Азия/Баку 3:19:24 LMT 1924 2 мая
Зона Америка/Бойсе -7:44:49 LMT 1883 18 ноя 12:15:11
Зона Европа/Андорра 0:06:04 LMT 1901

12. Смещение часового пояса составляет от -12 часов до +12 часов
Время островов Лайн: UTC+14.

13. Дата и время в прошлом неизменны
Данные о часовом поясе могут быть неверными, и их исправляют. Таким образом, дата и время в прошлом могут измениться. Примером является зона Азия/Шанхай. А отсюда можно скачать данные часовых поясов и в файле NEWS найти раздел "Changes to past timestamps" с другими примерами.

14. Летнее время всегда -1 или +1 час
В зоне Австралия/Лорд Хоув стрелки переводят на 30 минут. А в зоне Антарктида/Тролл – на 2 часа.

15. Дни начинаются в полночь
Переход на полночь произошёл между 1920 и 1930 годами. Исторически день начинался в полдень. Это было полезно для астрономов, которые могли записывать свои наблюдения в один день. Например, если вы наблюдаете звезду в 23:59, вы можете записать ее в тот же день, что и наблюдение в 00:01.
День в раввинистическом еврейском календаре длится от заката (начало «вечера») до следующего заката.

16. Полночь - действительное время
В Чили летнее время начинается в полночь, поэтому полночь - недопустимое время в этот день.
2019-09-07 23:59:59
2019-09-08 01:00:00

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

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

19. В одном и том же месяце всегда одинаковое количество дней
В Швеции есть 30 февраля 1712 года. Эта дата действительна только в Швеции.

20. 1 минута = 60 секунд
Период вращения Земли меняется со временем. Как правило, изменение составляет около секунды каждые пару лет. Всякий раз, когда отклонение от атомного времени становится слишком большим, вводится поправка на одну секунду, называемая дополнительной секундой. Это сделано таким образом, чтобы разница никогда не превышала 0,9 секунды. Например, 30 июня 2012 г., 23:59:60 — допустимое время.
До Windows Server 2016 служба времени Microsoft не знала о секундах координации, но полагалась на внешнюю службу времени, чтобы позаботиться об этом. Сейчас Windows «знает» о дополнительных секундах.

21. Дни длятся 24 часа
Япония соблюдала летнее время в 1948-1951 гг. Однако они используют стратегию, которая сильно отличается от стратегии других стран. Они просто добавляют час в конце дня:
Суббота 23:58
Суббота 23:59
Суббота 24:00 (без изменения даты)
Суббота 24:01
...
Суббота 24:58
Суббота 24:59
Воскресенье 00:00
Воскресенье 00:01

22. Сломанные часы показывают правильное время дважды в день
Оно может быть правильным 3 раза в день или 1 раз из-за перехода на летнее время.

23. У небольшой страны 1 часовой пояс
Во Франции 12 часовых поясов (13, включая ее притязания на Антарктиду) от UTC-11 до UTC+12. Список часовых поясов по странам можно найти в Википедии.

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

Источник: https://www.meziantou.net/misconceptions-about-date-and-time.htm
👍20