День девятьсот восемьдесят пятый. #ЗаметкиНаПолях
Правильное Использование Ключевого Слова static в C#
Основная проблема в том, что static плохо сочетается с изменяемым состоянием, т.е. состоянием, которое может изменяться в течение жизненного цикла программы. Как следствие правила использования следующие:
1. Статические поля должны быть только для чтения (по возможности, константами).
2. Статические поля должны ссылаться на неизменяемое состояние.
3. Статические методы должны быть чистыми функциями, то есть вычислять результат из входных параметров, без изменения состояния входных данных или любого другого состояния.
4. Статические классы допустимы, если их поля и методы соответствуют этим рекомендациям.
Основная мотивация для создания статического поля - создать объект, который живет в течение всего времени выполнения программы. Часто это важный объект, например контекст, доступ к которому осуществляется многими методами. На первый взгляд, сделать его статическим и видимым всем удобнее, чем передавать везде как параметр. Посмотрим, какие проблемы это сулит.
1. Код должен выполняться в пределах сессии
Объект, живущий всё время, пока исполняется программа, зачастую не нужен. На самом деле состояние требуется в пределах «сессии»: в течение веб-запроса или во время выполнения тестов. При этом совместное использование изменяемого состояния – не лучшая идея. Все «сессии» теперь должны заботиться о том, чтобы общее состояние было правильно инициализировано и правильно финализировано. Кроме того, веб-запросы де-факто выполняются конкурентно. Даже тесты можно запустить параллельно. В такой ситуации вам необходимо синхронизировать доступ к статическому изменяемому состоянию. А синхронизация доступа - это не только боль, но и убийца производительности.
2. Управление состоянием в конкурентной среде
В этом случае использование одного ресурса на поток устраняет необходимость в синхронизации. Идея в том, что к объекту всегда обращается один и тот же поток, т.е. каждый поток имеет свой собственный экземпляр. Вы можете использовать для этого статическое поле, помеченное атрибутом
3. Глобальное состояние и поддержка
Статические поля представляют глобальное состояние, доступ к которому можно получить из любого места кода. И разработчики могут создать зависимость от глобального состояния где угодно. Со временем это неизбежно приведёт к неуправляемому доступу к глобальному состоянию, и результатом будет подверженный ошибкам код, неконтролируемые побочные эффекты и утомительные сеансы отладки.
4. Чистые функции
Часто классы содержат функции, которые вычисляют результат без использования полей экземпляра объекта. Эти функции могут быть объявлены статическими. В результате получаются жирные классы, которые трудно понять и поддерживать. Поэтому в случае сложной логики лучше размещать такую реализацию во вспомогательных статических классах. Это не только делает классы экземпляров меньше и понятнее, но и сами статические классы хорошо поддаются тестированию: тесту нужно инициализировать входные данные, вызвать статический метод и проверить результат. Нет никакого скрытого состояния, о котором стоит беспокоиться, не нужно создавать объекты-имитации.
А методы расширения помогают не только отделить объект от его поведения, но и сделать вызовы более естественными.
Итого
Ключевое слово static плохо сочетается с изменяемым состоянием. Неправильное использование обязательно приведёт к серьёзным неприятностям. Но оно может стать серьёзным подспорьем, если использовать его с умом.
Источник: https://blog.ndepend.com/the-proper-usages-of-the-keyword-static-in-c/
Правильное Использование Ключевого Слова static в C#
Основная проблема в том, что static плохо сочетается с изменяемым состоянием, т.е. состоянием, которое может изменяться в течение жизненного цикла программы. Как следствие правила использования следующие:
1. Статические поля должны быть только для чтения (по возможности, константами).
2. Статические поля должны ссылаться на неизменяемое состояние.
3. Статические методы должны быть чистыми функциями, то есть вычислять результат из входных параметров, без изменения состояния входных данных или любого другого состояния.
4. Статические классы допустимы, если их поля и методы соответствуют этим рекомендациям.
Основная мотивация для создания статического поля - создать объект, который живет в течение всего времени выполнения программы. Часто это важный объект, например контекст, доступ к которому осуществляется многими методами. На первый взгляд, сделать его статическим и видимым всем удобнее, чем передавать везде как параметр. Посмотрим, какие проблемы это сулит.
1. Код должен выполняться в пределах сессии
Объект, живущий всё время, пока исполняется программа, зачастую не нужен. На самом деле состояние требуется в пределах «сессии»: в течение веб-запроса или во время выполнения тестов. При этом совместное использование изменяемого состояния – не лучшая идея. Все «сессии» теперь должны заботиться о том, чтобы общее состояние было правильно инициализировано и правильно финализировано. Кроме того, веб-запросы де-факто выполняются конкурентно. Даже тесты можно запустить параллельно. В такой ситуации вам необходимо синхронизировать доступ к статическому изменяемому состоянию. А синхронизация доступа - это не только боль, но и убийца производительности.
2. Управление состоянием в конкурентной среде
В этом случае использование одного ресурса на поток устраняет необходимость в синхронизации. Идея в том, что к объекту всегда обращается один и тот же поток, т.е. каждый поток имеет свой собственный экземпляр. Вы можете использовать для этого статическое поле, помеченное атрибутом
ThreadStatic
. Конкурентный доступ при этом исключается, но каждый поток по-прежнему должен беспокоиться об управлении состоянием. Поэтому создание выделенного объекта на поток более элегантно, чем статическое поле с атрибутом ThreadStatic
: так вы можете правильно отслеживать историю изменений состояния.3. Глобальное состояние и поддержка
Статические поля представляют глобальное состояние, доступ к которому можно получить из любого места кода. И разработчики могут создать зависимость от глобального состояния где угодно. Со временем это неизбежно приведёт к неуправляемому доступу к глобальному состоянию, и результатом будет подверженный ошибкам код, неконтролируемые побочные эффекты и утомительные сеансы отладки.
4. Чистые функции
Часто классы содержат функции, которые вычисляют результат без использования полей экземпляра объекта. Эти функции могут быть объявлены статическими. В результате получаются жирные классы, которые трудно понять и поддерживать. Поэтому в случае сложной логики лучше размещать такую реализацию во вспомогательных статических классах. Это не только делает классы экземпляров меньше и понятнее, но и сами статические классы хорошо поддаются тестированию: тесту нужно инициализировать входные данные, вызвать статический метод и проверить результат. Нет никакого скрытого состояния, о котором стоит беспокоиться, не нужно создавать объекты-имитации.
А методы расширения помогают не только отделить объект от его поведения, но и сделать вызовы более естественными.
Select
, Where
, First
и т.п. - это статические методы расширения для IEnumerable
, которые не изменяют входную последовательность, а возвращают новую.Итого
Ключевое слово static плохо сочетается с изменяемым состоянием. Неправильное использование обязательно приведёт к серьёзным неприятностям. Но оно может стать серьёзным подспорьем, если использовать его с умом.
Источник: https://blog.ndepend.com/the-proper-usages-of-the-keyword-static-in-c/
День девятьсот восемьдесят шестой. #Курсы
Бесплатная Неделя на Pluralsight
Налетай! Торопись! Изучай! Левел-апсь!)))
Pluralsight объявили неделю бесплатного доступа ко всем своим курсам. До понедельника, 18 октября 7 утра по Москве все курсы, видео и проекты можно получить абсолютно бесплатно.
Зарегистрироваться можно здесь https://www.pluralsight.com/offer/2021/q4-free-week
Если кому интересно, чтоб не искать, вот ссылки на мою подборку курсов (работают после регистрации на Pluralsight):
- ASP .NET Core (для экзамена 70-486) https://app.pluralsight.com/channels/details/e6a5dd31-af53-4320-b160-7c4e712b06cb
- Azure Fundamentals (для экзамена AZ-900) https://app.pluralsight.com/paths/certificate/microsoft-azure-fundamentals-az-900
- ASP .NET API, Blazor и Azure https://app.pluralsight.com/channels/details/1e5510d8-c21c-4590-b639-08dc23020281
- Прочее https://app.pluralsight.com/channels/details/b5f23d94-9914-45ba-9bd7-23ea00b5769f
Бесплатная Неделя на Pluralsight
Налетай! Торопись! Изучай! Левел-апсь!)))
Pluralsight объявили неделю бесплатного доступа ко всем своим курсам. До понедельника, 18 октября 7 утра по Москве все курсы, видео и проекты можно получить абсолютно бесплатно.
Зарегистрироваться можно здесь https://www.pluralsight.com/offer/2021/q4-free-week
Если кому интересно, чтоб не искать, вот ссылки на мою подборку курсов (работают после регистрации на Pluralsight):
- ASP .NET Core (для экзамена 70-486) https://app.pluralsight.com/channels/details/e6a5dd31-af53-4320-b160-7c4e712b06cb
- Azure Fundamentals (для экзамена AZ-900) https://app.pluralsight.com/paths/certificate/microsoft-azure-fundamentals-az-900
- ASP .NET API, Blazor и Azure https://app.pluralsight.com/channels/details/1e5510d8-c21c-4590-b639-08dc23020281
- Прочее https://app.pluralsight.com/channels/details/b5f23d94-9914-45ba-9bd7-23ea00b5769f
День девятьсот восемьдесят седьмой. #ЗаметкиНаПолях
Основные Заблуждения о Внедрении Зависимостей в ASP.NET Core. Начало
Разработчики, которые недавно начали работать с внедрением зависимостей в ASP.NET Core, часто неверно понимают принципы его работы. Некоторые заблуждения вызывают ошибки, а другие приводят к избыточному коду. Рассмотрим наиболее распространённые.
1. Scoped сервис создаётся только один раз для каждого веб-запроса
Разработчики могут выбрать время жизни сервиса при внедрении зависимости:
- Transient - новый экземпляр класса создается каждый раз, когда он запрашивается из контейнера DI,
- Singleton - один экземпляр класса будет создан на всё время существования приложения,
- Scoped - экземпляр класса должен быть создан один раз для каждого веб-запроса.
Область действия - это область кода между созданием экземпляра типа
Если один и тот же тип в области действия запрашивается несколько раз, всегда будет возвращаться один и тот же экземпляр. ASP.NET Core создаёт область действия в начале веб-запроса и удаляет её в конце веб-запроса. Поэтому экземпляры, ограниченные областью действия, создаются один раз для каждого веб-запроса. Но не всегда.
Ничто не мешает разработчикам создавать дочерние области действия. Экземпляры одного и того же типа, разрешённые из разных областей действия, также будут разными:
Продолжение следует…
Источник: https://levelup.gitconnected.com/top-misconceptions-about-dependency-injection-in-asp-net-core-c6a7afd14eb4
Основные Заблуждения о Внедрении Зависимостей в ASP.NET Core. Начало
Разработчики, которые недавно начали работать с внедрением зависимостей в ASP.NET Core, часто неверно понимают принципы его работы. Некоторые заблуждения вызывают ошибки, а другие приводят к избыточному коду. Рассмотрим наиболее распространённые.
1. Scoped сервис создаётся только один раз для каждого веб-запроса
Разработчики могут выбрать время жизни сервиса при внедрении зависимости:
- Transient - новый экземпляр класса создается каждый раз, когда он запрашивается из контейнера DI,
- Singleton - один экземпляр класса будет создан на всё время существования приложения,
- Scoped - экземпляр класса должен быть создан один раз для каждого веб-запроса.
services.AddScoped<IUserRepository, UserRepository>();Однако время жизни, ограниченное областью действия, (scoped) не гарантирует, что класс создаётся только один раз для каждого веб-запроса. Это означает, что класс будет создан только один раз в заданной области действия, но в одном веб-запросе можно создать несколько областей.
Область действия - это область кода между созданием экземпляра типа
IServiceScope
и вызовом его метода Dispose. IServiceScope
содержит свойство ServiceProvider
, которое используется для разрешения экземпляров сервисов, зарегистрированных в методе ConfigureServices
.Если один и тот же тип в области действия запрашивается несколько раз, всегда будет возвращаться один и тот же экземпляр. ASP.NET Core создаёт область действия в начале веб-запроса и удаляет её в конце веб-запроса. Поэтому экземпляры, ограниченные областью действия, создаются один раз для каждого веб-запроса. Но не всегда.
Ничто не мешает разработчикам создавать дочерние области действия. Экземпляры одного и того же типа, разрешённые из разных областей действия, также будут разными:
public class SomeServiceБывают случаи, когда создание области вручную - это единственный правильный способ получить экземпляр сервиса, например, для разрешения scoped сервиса из синглтона. Знание особенностей областей действия может защитить вас от множества проблем.
{
private readonly IServiceScopeFactory _factory;
public SomeService(IServiceScopeFactory factory)
=> _factory = factory;
public void DoSomething()
{
using (IServiceScope scope = _factory.CreateScope());
var obj1 =
scope.ServiceProvider.GetService<IUserRepository>();
using (IServiceScope childScope =
_factory.CreateScope())
{
var obj2 = childScope.ServiceProvider
.GetService<IUserRepository>();
if (obj1 != obj2)
Console.WriteLine("Экземпляры разные.");
}
}
}
Продолжение следует…
Источник: https://levelup.gitconnected.com/top-misconceptions-about-dependency-injection-in-asp-net-core-c6a7afd14eb4
День девятьсот восемьдесят восьмой. #ЗаметкиНаПолях
Основные Заблуждения о Внедрении Зависимостей в ASP.NET Core. Продолжение
Начало
2. DI фреймворк всегда проверяет захват зависимостей
Захват зависимостей - это своего рода антипаттерн внедрения зависимостей, который может привести к ошибкам в вашем коде.
Всё, что вам нужно сделать, чтобы получить проблему с захватом зависимости, - это внедрить класс с более коротким временем жизни в класс с более длительным временем жизни. Например, внедрите сервис, ограниченный областью действия, в синглтон. В этом случае класс, ограниченный областью действия, не будет создаваться и удаляться для каждого веб-запроса. Он будет иметь то же время жизни, что и синглтон, и фактически станет синглтоном.
Почему это действительно проблема?
В веб-приложениях экземпляры синглтонов, хранящие, например, кэшированные данные, спроектированы для обеспечения потоковой безопасности, чтобы избежать ошибок параллелизма, когда несколько запросов обращаются к одному и тому же синглтон объекту. Однако классы, ограниченные областью действия, не обязательно должны быть потокобезопасными, если разработчики не запускают дополнительные параллельные потоки в веб-запросе.
Проблема захвата зависимости продлевает время жизни объекта. Если потоконебезопасный класс, предназначенный для жизни внутри области действия, становится синглтоном, в приложении будут возникать ошибки параллелизма.
ASP.NET Core защищает разработчиков от этой проблемы, проверяя граф зависимостей на наличие захвата зависимостей и генерируя исключение, если они обнаружены.
Заблуждение состоит в том, что проверка ASP.NET Core на наличие захвата зависимостей всегда будет работать. Однако в ядре ASP.NET проверка зависимостей по умолчанию выполняется только в среде разработки (Development).
Если код не был хорошо протестирован локально, в других средах, кроме среды разработки, исключения не возникнет. Вместо этого время жизни сервисов будет без предупреждения увеличено, что вызовет потенциальные проблемы с параллелизмом и возможно другие.
Чтобы гарантировать, что код всегда проверяется на наличие захвата зависимостей, установите для параметра
Окончание следует…
Источник: https://levelup.gitconnected.com/top-misconceptions-about-dependency-injection-in-asp-net-core-c6a7afd14eb4
Основные Заблуждения о Внедрении Зависимостей в ASP.NET Core. Продолжение
Начало
2. DI фреймворк всегда проверяет захват зависимостей
Захват зависимостей - это своего рода антипаттерн внедрения зависимостей, который может привести к ошибкам в вашем коде.
Всё, что вам нужно сделать, чтобы получить проблему с захватом зависимости, - это внедрить класс с более коротким временем жизни в класс с более длительным временем жизни. Например, внедрите сервис, ограниченный областью действия, в синглтон. В этом случае класс, ограниченный областью действия, не будет создаваться и удаляться для каждого веб-запроса. Он будет иметь то же время жизни, что и синглтон, и фактически станет синглтоном.
Почему это действительно проблема?
В веб-приложениях экземпляры синглтонов, хранящие, например, кэшированные данные, спроектированы для обеспечения потоковой безопасности, чтобы избежать ошибок параллелизма, когда несколько запросов обращаются к одному и тому же синглтон объекту. Однако классы, ограниченные областью действия, не обязательно должны быть потокобезопасными, если разработчики не запускают дополнительные параллельные потоки в веб-запросе.
Проблема захвата зависимости продлевает время жизни объекта. Если потоконебезопасный класс, предназначенный для жизни внутри области действия, становится синглтоном, в приложении будут возникать ошибки параллелизма.
ASP.NET Core защищает разработчиков от этой проблемы, проверяя граф зависимостей на наличие захвата зависимостей и генерируя исключение, если они обнаружены.
public void ConfigureServices(IServiceCollection services)Выполнение приведенного выше кода приведёт к исключению
{
services.AddSingleton<UserService>();
services.AddScoped<IUserRepository, UserRepository>();
}
public class UserService
{
public UserService(IUserRepository userRepository)
{
}
}
public class UserRepository : IUserRepository
{
}
InvalidOperationException
, из-за того, что класс, ограниченный областью действия, IUserRepository
не может быть использован из синглтона UserService
.Заблуждение состоит в том, что проверка ASP.NET Core на наличие захвата зависимостей всегда будет работать. Однако в ядре ASP.NET проверка зависимостей по умолчанию выполняется только в среде разработки (Development).
Если код не был хорошо протестирован локально, в других средах, кроме среды разработки, исключения не возникнет. Вместо этого время жизни сервисов будет без предупреждения увеличено, что вызовет потенциальные проблемы с параллелизмом и возможно другие.
Чтобы гарантировать, что код всегда проверяется на наличие захвата зависимостей, установите для параметра
ValidateScopes
значение true в точке входа приложения:public static IHostBuilder CreateHostBuilder(string[] args) =>Также вы можете указать свойство
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.UseDefaultServiceProvider((context, options) => {
options.ValidateScopes = true;
});
options.ValidateOnBuild = true;Эта функция добавлена в .NET Core 3.0. Проверка сервисов будет производиться во время построения провайдера сервисов, т.е. во время старта приложения, а не при запросе сервиса, который невозможно разрешить.
Окончание следует…
Источник: https://levelup.gitconnected.com/top-misconceptions-about-dependency-injection-in-asp-net-core-c6a7afd14eb4
День девятьсот восемьдесят девятый. #ЗаметкиНаПолях
Основные Заблуждения о Внедрении Зависимостей в ASP.NET Core. Окончание
Начало
Продолжение
3. Контейнер DI всегда удаляет объекты
Контейнер DI отвечает не только за создание экземпляров объектов, но и за их удаление (dispose). Временные сервисы и сервисы, ограниченные областью действия, удаляются в конце области (в конце веб-запроса в ASP.NET Core), а классы-синглтоны удаляются перед завершением работы приложения. Контейнер DI полностью управляет временем жизни сервисов приложения. Разработчикам не нужно заботиться о вызове метода Dispose.
Однако в редких случаях разработчикам может потребоваться создать экземпляр класса самостоятельно, а затем предоставить экземпляр в контейнер DI:
4. Контейнер DI принимает только классы с интерфейсами
Внедрение зависимостей через конструктор довольно часто реализуется с использованием интерфейсов. Вот почему некоторые разработчики ошибочно полагают, что класс должен иметь интерфейс для регистрации в контейнере внедрения зависимостей. Это заблуждение приводит к извлечению интерфейса для классов, которым на самом деле интерфейс не нужен. В следующем примере показано, как зарегистрировать класс, не имеющий интерфейса, в контейнере DI:
Источник: https://levelup.gitconnected.com/top-misconceptions-about-dependency-injection-in-asp-net-core-c6a7afd14eb4
Основные Заблуждения о Внедрении Зависимостей в ASP.NET Core. Окончание
Начало
Продолжение
3. Контейнер DI всегда удаляет объекты
Контейнер DI отвечает не только за создание экземпляров объектов, но и за их удаление (dispose). Временные сервисы и сервисы, ограниченные областью действия, удаляются в конце области (в конце веб-запроса в ASP.NET Core), а классы-синглтоны удаляются перед завершением работы приложения. Контейнер DI полностью управляет временем жизни сервисов приложения. Разработчикам не нужно заботиться о вызове метода Dispose.
Однако в редких случаях разработчикам может потребоваться создать экземпляр класса самостоятельно, а затем предоставить экземпляр в контейнер DI:
public void ConfigureServices(IServiceCollection services)В этом случае объект не будет удалён, как могли ожидать разработчики. Здесь им нужно не забыть удалить экземпляр самостоятельно, чтобы избежать проблем, которые могут возникнуть из-за не удалённых ресурсов. Разработчики могут просто ошибочно использовать приведённый выше синтаксис для регистрации объекта вместо следующего:
{
services.AddSingleton(new Service());
}
services.AddSingleton<Service>();В последнем случае объект будет создан, а затем удалён контейнером DI, что является лучшим вариантом. Оба варианта очень похожи, и легко случайно использовать не тот. Будьте осторожны.
4. Контейнер DI принимает только классы с интерфейсами
Внедрение зависимостей через конструктор довольно часто реализуется с использованием интерфейсов. Вот почему некоторые разработчики ошибочно полагают, что класс должен иметь интерфейс для регистрации в контейнере внедрения зависимостей. Это заблуждение приводит к извлечению интерфейса для классов, которым на самом деле интерфейс не нужен. В следующем примере показано, как зарегистрировать класс, не имеющий интерфейса, в контейнере DI:
public class SomeClassВнедрение класса в сервис:
{
}
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<SomeClass>();
}
public class SomeServiceИнтерфейсы - отличная функция, которая позволяет вам определить контракт для типа, добиться слабой связи, заменить типы во время выполнения, облегчить тестируемость и т.д. Однако в случаях, когда зависимость между классами приемлема, лучше не загрязнять код, создавая избыточные интерфейсы.
{
public SomeService(SomeClass someClass) {}
}
Источник: https://levelup.gitconnected.com/top-misconceptions-about-dependency-injection-in-asp-net-core-c6a7afd14eb4
День девятьсот девяностый. #Оффтоп
Гугли Как Профессионал. 10 Советов по Эффективному Поиску
На старте карьеры многие программисты стесняются искать что-то в сети и в основном полагаются на свои скудные знания. Чем больше опыта мы приобретаем, тем больше понимаем, что запоминать детали синтаксиса каждого языка (а обычно за несколько лет карьеры приходится таки попробовать разные языки) или алгоритмы разворачивания деревьев – это пустая трата времени и «памяти». Поэтому всё большую ценность приобретает умение быстро найти решение в гугле.
1. Точный поиск
Начнём с простого. Поискать полный текст, а не по каждому слову отдельно (например, полный текст ошибки, которую выдал компилятор), можно, заключив текст в кавычки. В противном случае гугл включит в результаты поиск по отдельным словам и синонимам.
2. Поиск на определённом сайте
Добавьте в строку поиска параметр
Добавьте ключевое слово со знаком минус. Например:
4. Новые/старые результаты
Чтобы получать результаты до/после определённого года, можно добавить параметры
5. Любой интервал
Параметры before и after можно использовать совместно, и задать интервал, например,
6. Выбор варианта
Можно использовать знак
Может показаться, что использование подстановки для «чего угодно» - бессмысленно, однако это позволяет, например, найти все поддомены у сайта, кроме www:
Тоже довольно широко известный параметр. Добавьте
Используйте параметр
10. Поиск в кэше
Параметр
Источник: https://youtu.be/cEBkvm0-rg0
Гугли Как Профессионал. 10 Советов по Эффективному Поиску
На старте карьеры многие программисты стесняются искать что-то в сети и в основном полагаются на свои скудные знания. Чем больше опыта мы приобретаем, тем больше понимаем, что запоминать детали синтаксиса каждого языка (а обычно за несколько лет карьеры приходится таки попробовать разные языки) или алгоритмы разворачивания деревьев – это пустая трата времени и «памяти». Поэтому всё большую ценность приобретает умение быстро найти решение в гугле.
1. Точный поиск
Начнём с простого. Поискать полный текст, а не по каждому слову отдельно (например, полный текст ошибки, которую выдал компилятор), можно, заключив текст в кавычки. В противном случае гугл включит в результаты поиск по отдельным словам и синонимам.
2. Поиск на определённом сайте
Добавьте в строку поиска параметр
site:URL
, например: nullreferenceexception site:stackoverflow.com
3. Исключить из результатовДобавьте ключевое слово со знаком минус. Например:
get dom element -jqueryК примеру, так можно исключить имя вашего класса из полного текста ошибки.
4. Новые/старые результаты
Чтобы получать результаты до/после определённого года, можно добавить параметры
after:2005
или before:2015
. Полезно, чтобы искать, например, документацию по .NET Framework.5. Любой интервал
Параметры before и after можно использовать совместно, и задать интервал, например,
2013..2019
. Хотя, интервал в принципе может быть любым, вроде $1000..$300000
.6. Выбор варианта
Можно использовать знак
|
для выбора между вариантами. Также, если кроме этих вариантов в запросе есть другие слова, можно заключить выбор в скобки, как в выражении regex:(A|B) C7. Wildcard
Может показаться, что использование подстановки для «чего угодно» - бессмысленно, однако это позволяет, например, найти все поддомены у сайта, кроме www:
site:*.microsoft.com -www8. Тип файла
Тоже довольно широко известный параметр. Добавьте
filetype
к запросу, чтобы искать только определённый тип файлов, например: filetype:pdf
9. Аналогичные сайтыИспользуйте параметр
related:URL
, чтобы найти сайты, аналогичные заданному. Например: related:google.com
позволит найти альтернативные поисковики.10. Поиск в кэше
Параметр
cache:url
позволяет поискать сайт в кэше. Например, узнать, когда страница в последний раз была кэширована гуглом.Источник: https://youtu.be/cEBkvm0-rg0
👍3
День девятьсот девяносто первый. #ЗаметкиНаПолях #AsyncTips
Ожидание завершения группы задач
Задача: есть несколько задач, и нужно подождать, пока они все завершатся.
Решение
Для этой цели существует метод
Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 2.
Ожидание завершения группы задач
Задача: есть несколько задач, и нужно подождать, пока они все завершатся.
Решение
Для этой цели существует метод
Task.WhenAll
. Он получает несколько задач и возвращает задачу, которая завершается при завершении всех указанных задач.Task task1 = Task.Delay(100);Если все задачи имеют одинаковый тип результата и все завершаются успешно, то задача
Task task2 = Task.Delay(200);
Task task3 = Task.Delay(150);
await Task.WhenAll(task1, task2, task3);
Task.WhenAll
возвращает массив, содержащий результаты всех задач:Task<int> task1 = Task.FromResult(3);Есть перегруженная версия
Task<int> task2 = Task.FromResult(5);
Task<int> task3 = Task.FromResult(7);
int[] results = await Task.WhenAll(task1, task2, task3);
// "results" содержит { 3, 5, 7 }
Task.WhenAll
, которая получает IEnumerable
с задачами. Тем не менее заметьте, что при использовании LINQ код получается более понятным, когда последовательность явно материализуется:async Task<string> GetAllAsync(Если одна из задач выбросит исключение, то
HttpClient client,
IEnumerable<string> urls)
{
// Определяем действие для каждого URL
var downloads = urls.Select(url => client.GetStringAsync(url));
// Задачи ещё не запущены, т.к.
// последовательность не материализована
// Запускаем загрузку для всех URL одновременно
Task<string>[] downloadTasks = downloads.ToArray();
// Все задачи запущены.
// Асинхронно ожидаем завершения всех загрузок
string[] htmlPages =
await Task.WhenAll(downloadTasks);
return string.Concat(htmlPages);
}
Task.WhenAll
вернёт задачу в статусе Faulted
с этим исключением. Если сразу несколько задач выдают исключения, то все они помещаются в задачу, возвращаемую Task.WhenAll
. При ожидании этой задачи будет выдано только одно из них:tryОбычно первого «попавшегося» исключения достаточно, но, если вам нужны все исключения, проверьте свойство
{
await Task.WhenAll(task1, task2);
}
catch (Exception ex)
{
// "ex" – исключение либо из task1, либо из task2
...
}
Exception
задачи, возвращаемой Task.WhenAll
:Task allTasks = Task.WhenAll(task1, task2);Если ни одна задача не выбросила исключения, но хотя бы одна задача была отменена, то задача, возвращённая
try
{
await Task.WhenAll(allTasks);
}
catch
{
AggregateException allExceptions = allTasks.Exception;
...
}
Task.WhenAll
, будет в статусе Canceled
.Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 2.
👍1
День девятьсот девяносто второй. #ЗаметкиНаПолях #AsyncTips
Отмена Группы Задач
Вчерашний пост вызвал в чате обсуждение. Возник такой вопрос: если одна из ожидаемых задач выбросит исключение, можно ли отменить остальные? Уточню вводные:
- задачи формально не связаны, но результат нужен или их всех, или никакого;
- задачи нужно выполнять параллельно.
Первой моей идеей было передать во все задачи один токен отмены, и в случае исключения отменять все задачи одним токеном. Проблема в том, что сам токен не позволяет инициировать отмену (он только реагирует на отмену), поэтому в задачи нужно передавать
Итак, у нас есть «хорошие» и «плохие» задачи (передаём в них время задержки delay для удобства тестирования):
Далее создаём несколько задач, «оборачивая» их в наш метод
Запускаем:
Источник: https://stackoverflow.com/questions/19394150/tpl-execute-dependent-task-and-stop-all-task-when-some-task-fails#19397190
Отмена Группы Задач
Вчерашний пост вызвал в чате обсуждение. Возник такой вопрос: если одна из ожидаемых задач выбросит исключение, можно ли отменить остальные? Уточню вводные:
- задачи формально не связаны, но результат нужен или их всех, или никакого;
- задачи нужно выполнять параллельно.
Первой моей идеей было передать во все задачи один токен отмены, и в случае исключения отменять все задачи одним токеном. Проблема в том, что сам токен не позволяет инициировать отмену (он только реагирует на отмену), поэтому в задачи нужно передавать
CancellationTokenSource
. В итоге, поискав решение, я нашёл интересный пост на StackOverflow, кратким описанием которого хочу поделиться. Извините за однобуквенные имена, сокращал, как мог. Полный код по ссылке ниже.Итак, у нас есть «хорошие» и «плохие» задачи (передаём в них время задержки delay для удобства тестирования):
async Task<object> DoAsync(string id,Следующий метод «оборачивает» задачу, перехватывая исключения в ней и вызывая отмену остальных:
CancellationToken token, int delay)
{
await Task.Delay(delay, token);
return id;
}
async Task<object> BadAsync(string id,
CancellationToken token, int delay)
{
await Task.Delay(delay, token);
throw new Exception(id);
}
async Task<T> Wrap<T>(Task<T> task,Теперь собственно метод, выполняющий все задачи:
CancellationTokenSource cts)
{
try {
return await task;
}
catch {
if (!cts.IsCancellationRequested)
cts.Cancel(); // отменяем остальные
throw;
}
}
public async Task DoWorkAsync(CancellationToken t)Здесь мы используем внешний токен отмены
{
var tasks = new List<Task<object>>();
var cts = new CancellationTokenSource();
var ct = cts.Token;
try {
using (t.Register(() => cts.Cancel())) {
// параллельно запускаем задачи
tasks.Add(Wrap(DoAsync("1", ct, 500), cts));
tasks.Add(Wrap(DoAsync("2", ct, 1000), cts));
tasks.Add(Wrap(DoAsync("3", ct, 1500), cts));
tasks.Add(Wrap(BadAsync("Bad", ct, 700), cts));
await Task.WhenAll(tasks.ToArray());
}
}
catch (Exception e) {
Console.WriteLine(e.Message);
}
Console.WriteLine("Results:");
tasks.ForEach((task) =>
Console.WriteLine(task.Status));
}
t
, чтобы иметь возможность отменить все задачи извне. В директиве using регистрируем действие, которое будет выполнено при отмене внешнего токена.Далее создаём несколько задач, «оборачивая» их в наш метод
Wrap
. Передаём в них один токен отмены и разные времена задержки, имитируя асинхронную работу. Наша «плохая» задача выбросит исключение после задачи 1, но перед завершением задач 2 и 3, метод Wrap
перехватит исключение и отменит задачи 2 и 3.Запускаем:
var cts = new CancellationTokenSource(10000);Подробный код и обработка различных вариантов завершения группы задач по ссылке ниже.
DoWorkAsync(cts.Token).Wait();
//Вывод (в порядке регистрации задач):
RanToCompletion
Canceled
Canceled
Faulted
Источник: https://stackoverflow.com/questions/19394150/tpl-execute-dependent-task-and-stop-all-task-when-some-task-fails#19397190
👍2
День девятьсот девяносто третий. #Testing
7 Принципов Тестирования ПО
Опять про тестирование? Да. На этот раз глазами QA. Однако сегодня рассмотрим не советы о том, как тестировать, а общие принципы тестирования. То, чего следует и не следует ожидать при написании тестов.
1. Тестирование показывает наличие дефектов, а не их отсутствие
Многие тестировщики могли попадать в такую ситуацию: владелец продукта или даже клиент говорят, что ваша задача - гарантировать, что в результатах спринта нет ошибок.
Тестирование - это деструктивное действие, его цель - вывести программу из строя. Когда тестировщик пишет тестовый пример, он предназначен для поиска ошибки, а не для доказательства её отсутствия.
Итак, этот принцип гласит, что невозможно гарантировать отсутствие дефектов в объекте тестирования. Этот принцип не предназначен для оправдания недостаточно качественного тестирования и пропущенных ошибок. Он говорит о том, что нужно избегать ложных ожиданий в отношении продукта и результатов тестирования.
2. Исчерпывающее тестирование невозможно
У вас, вероятно, никогда не будет времени протестировать все возможные сценарии в продукте. Представьте себе небольшую форму с 3 полями выбора, каждое с 10 вариантами. Чтобы проверить все возможные сценарии, вам потребуется 1000 тестов.
Мы можем использовать метод тестирования, называемый эквивалентным разбиением. Вкратце, он состоит из разделения тестовых данных на блоки таким образом, что ожидается, что все элементы одного блока будут обрабатываться одинаково.
А при острой нехватке времени на тесты рекомендуется провести анализ рисков и расставить приоритеты, какие тесты писать в первую очередь с учётом более высоких рисков для бизнеса.
3. Раннее тестирование экономит время и деньги
Работа по тестированию должна начинаться как можно раньше в проекте. Действия по тестированию не ограничиваются выполнением или автоматизацией end-to-end тестов, когда функционал готов. Тестировщик также может просматривать документацию и требования, чтобы не допустить перехода ошибок на более поздние этапы жизненного цикла разработки. Помните, что чем позже вы обнаружите проблему, тем больше будет стоить её устранение.
4. Дефекты объединяются
Этот принцип является применением закона Парето: для многих исходов примерно 80% последствий происходят от 20% причин.
При тестировании ПО высока вероятность того, что 80% проблем можно найти в 20% модулей/компонентов. Так что будьте осторожны, когда найдёте ошибку, потому что в той же функции могут скрываться и другие.
5. Остерегайтесь парадокса пестицидов
Многократное использование одной и той же смеси пестицидов для уничтожения насекомых со временем приведет к развитию у насекомых устойчивости к пестициду.
Если вы используете один и тот же приём тестирования снова и снова, скорее всего, он станет менее эффективным. Рассмотрите другой способ, который имеет более высокую вероятность обнаружения ошибок.
6. Тестирование зависит от контекста
Тестирование в agile проекте выполняется иначе, чем в waterfall проекте, из-за различий в периодичности выпуска.
Тестирование электронной коммерции проводится иначе, чем тестирование системы интернет-банкинга, из-за различных рисков.
Правильный инструмент для автоматизации регрессионных тестов также сильно зависит от контекста. По мере того, как вы набираетесь опыта, вы лучше понимаете контекст и лучше планируете работу по обеспечению качества, чтобы она соответствовала контексту.
7. Отсутствие ошибок - это заблуждение
Из второго принципа мы знаем, что тестирование показывает наличие дефектов, а не их отсутствие. Этот принцип гласит, что независимо от того, сколько усилий вы или ваша команда прилагаете к тестированию, сами по себе тесты никогда не могут гарантировать безошибочность ПО.
Считается, что ПО содержит ошибку только тогда, когда что-то работает не так, как ожидалось. Однако система, которая слишком сложна для пользователя (ошибки в удобстве использования) или не полностью соответствует потребностям пользователя (ошибки в моделировании), также считается имеющей ошибки.
Источник
7 Принципов Тестирования ПО
Опять про тестирование? Да. На этот раз глазами QA. Однако сегодня рассмотрим не советы о том, как тестировать, а общие принципы тестирования. То, чего следует и не следует ожидать при написании тестов.
1. Тестирование показывает наличие дефектов, а не их отсутствие
Многие тестировщики могли попадать в такую ситуацию: владелец продукта или даже клиент говорят, что ваша задача - гарантировать, что в результатах спринта нет ошибок.
Тестирование - это деструктивное действие, его цель - вывести программу из строя. Когда тестировщик пишет тестовый пример, он предназначен для поиска ошибки, а не для доказательства её отсутствия.
Итак, этот принцип гласит, что невозможно гарантировать отсутствие дефектов в объекте тестирования. Этот принцип не предназначен для оправдания недостаточно качественного тестирования и пропущенных ошибок. Он говорит о том, что нужно избегать ложных ожиданий в отношении продукта и результатов тестирования.
2. Исчерпывающее тестирование невозможно
У вас, вероятно, никогда не будет времени протестировать все возможные сценарии в продукте. Представьте себе небольшую форму с 3 полями выбора, каждое с 10 вариантами. Чтобы проверить все возможные сценарии, вам потребуется 1000 тестов.
Мы можем использовать метод тестирования, называемый эквивалентным разбиением. Вкратце, он состоит из разделения тестовых данных на блоки таким образом, что ожидается, что все элементы одного блока будут обрабатываться одинаково.
А при острой нехватке времени на тесты рекомендуется провести анализ рисков и расставить приоритеты, какие тесты писать в первую очередь с учётом более высоких рисков для бизнеса.
3. Раннее тестирование экономит время и деньги
Работа по тестированию должна начинаться как можно раньше в проекте. Действия по тестированию не ограничиваются выполнением или автоматизацией end-to-end тестов, когда функционал готов. Тестировщик также может просматривать документацию и требования, чтобы не допустить перехода ошибок на более поздние этапы жизненного цикла разработки. Помните, что чем позже вы обнаружите проблему, тем больше будет стоить её устранение.
4. Дефекты объединяются
Этот принцип является применением закона Парето: для многих исходов примерно 80% последствий происходят от 20% причин.
При тестировании ПО высока вероятность того, что 80% проблем можно найти в 20% модулей/компонентов. Так что будьте осторожны, когда найдёте ошибку, потому что в той же функции могут скрываться и другие.
5. Остерегайтесь парадокса пестицидов
Многократное использование одной и той же смеси пестицидов для уничтожения насекомых со временем приведет к развитию у насекомых устойчивости к пестициду.
Если вы используете один и тот же приём тестирования снова и снова, скорее всего, он станет менее эффективным. Рассмотрите другой способ, который имеет более высокую вероятность обнаружения ошибок.
6. Тестирование зависит от контекста
Тестирование в agile проекте выполняется иначе, чем в waterfall проекте, из-за различий в периодичности выпуска.
Тестирование электронной коммерции проводится иначе, чем тестирование системы интернет-банкинга, из-за различных рисков.
Правильный инструмент для автоматизации регрессионных тестов также сильно зависит от контекста. По мере того, как вы набираетесь опыта, вы лучше понимаете контекст и лучше планируете работу по обеспечению качества, чтобы она соответствовала контексту.
7. Отсутствие ошибок - это заблуждение
Из второго принципа мы знаем, что тестирование показывает наличие дефектов, а не их отсутствие. Этот принцип гласит, что независимо от того, сколько усилий вы или ваша команда прилагаете к тестированию, сами по себе тесты никогда не могут гарантировать безошибочность ПО.
Считается, что ПО содержит ошибку только тогда, когда что-то работает не так, как ожидалось. Однако система, которая слишком сложна для пользователя (ошибки в удобстве использования) или не полностью соответствует потребностям пользователя (ошибки в моделировании), также считается имеющей ошибки.
Источник
День девятьсот девяносто четвёртый. #ЗаметкиНаПолях
Сравнение Строк Сложнее, Чем Кажется. Начало
Сравнение строк отличается от сравнения чисел. 2 числа равны, если их значения идентичны. Для строк всё сложнее. Например, вы хотите сравнение с учетом регистра? А как насчёт разных способов написать одну и ту же букву? Например, в немецком языке часто встречается буква
В .NET есть 6 режимов сравнения строк:
1. Ordinal: выполняет простое сравнение байтов независимо от языка. Наиболее удобен для сравнения ресурсов, чувствительных к регистру, таких как пароли.
2. OrdinalIgnoreCase: обрабатывает символы в строках для сравнения, как если бы они были преобразованы в верхний регистр с использованием соглашений инвариантного языка и региональных параметров, а затем выполняет простое сравнение байтов, которое не зависит от языка. Подходит для сравнения строк, созданных программно, когда регистр не важен, таких как пути и имена файлов.
3. InvariantCulture: сравнивает строки лингвистически релевантным способом, но он не подходит для отображения в какой-либо конкретной культуре. Основное применение - упорядочивание строк таким образом, чтобы оно было идентичным для разных культур.
4. InvariantCultureIgnoreCase: сравнивает строки лингвистически релевантным способом, игнорируя регистр.
5. CurrentCulture: может использоваться, когда строки имеют лингвистическое значение (например, отображаются для пользователя или являются результатом взаимодействия с пользователем).
6. CurrentCultureIgnoreCase: может использоваться, когда строки являются лингвистически релевантными, а их регистр - нет.
Важно явно указывать режим сравнения, чтобы избежать неожиданного поведения или, что еще хуже, проблем с безопасностью. Например, если вы сравните 2 пароля, используя текущую культуру или инвариантную культуру, пароли могут быть одинаковыми, тогда как на самом деле они могут быть разными!
Примеры
Понять разницу между Ordinal и OrdinalIgnoreCase довольно просто. Сложнее понять культурно-зависимые сравнения.
Примечание: В примерах ниже последним параметром идёт перечисление
Источник: https://www.meziantou.net/string-comparisons-are-harder-than-it-seems.htm
Сравнение Строк Сложнее, Чем Кажется. Начало
Сравнение строк отличается от сравнения чисел. 2 числа равны, если их значения идентичны. Для строк всё сложнее. Например, вы хотите сравнение с учетом регистра? А как насчёт разных способов написать одну и ту же букву? Например, в немецком языке часто встречается буква
ß
, но её также можно написать ss
, так как это проще на многих клавиатурах.В .NET есть 6 режимов сравнения строк:
1. Ordinal: выполняет простое сравнение байтов независимо от языка. Наиболее удобен для сравнения ресурсов, чувствительных к регистру, таких как пароли.
2. OrdinalIgnoreCase: обрабатывает символы в строках для сравнения, как если бы они были преобразованы в верхний регистр с использованием соглашений инвариантного языка и региональных параметров, а затем выполняет простое сравнение байтов, которое не зависит от языка. Подходит для сравнения строк, созданных программно, когда регистр не важен, таких как пути и имена файлов.
3. InvariantCulture: сравнивает строки лингвистически релевантным способом, но он не подходит для отображения в какой-либо конкретной культуре. Основное применение - упорядочивание строк таким образом, чтобы оно было идентичным для разных культур.
4. InvariantCultureIgnoreCase: сравнивает строки лингвистически релевантным способом, игнорируя регистр.
5. CurrentCulture: может использоваться, когда строки имеют лингвистическое значение (например, отображаются для пользователя или являются результатом взаимодействия с пользователем).
6. CurrentCultureIgnoreCase: может использоваться, когда строки являются лингвистически релевантными, а их регистр - нет.
Важно явно указывать режим сравнения, чтобы избежать неожиданного поведения или, что еще хуже, проблем с безопасностью. Например, если вы сравните 2 пароля, используя текущую культуру или инвариантную культуру, пароли могут быть одинаковыми, тогда как на самом деле они могут быть разными!
Примеры
Понять разницу между Ordinal и OrdinalIgnoreCase довольно просто. Сложнее понять культурно-зависимые сравнения.
Примечание: В примерах ниже последним параметром идёт перечисление
StringComparison
. Для краткости я оставил лишь значение перечисления, поэтому подразумевается, что везде используется using static System.StringComparison;
// false
string.Equals("ss", "ß", OrdinalIgnoreCase);
// true в Windows / false в Linux (WSL) – об этом позже
string.Equals("ss", "ß", InvariantCulture);
\0
и ещё некоторые символы игнорируются в лингвистических сравнениях:// falseСравнение с учетом культуры сравнивает графемы. Например, строка
string.Equals("a\0b", "ab", Ordinal);
// true
string.Equals("a\0b", "ab", InvariantCulture);
// true
string.Equals("a\0b", "ab", CurrentCulture);
A\r\nB
разделена на A
, \r\n
и B
. Это означает, что \n
не является частью этой строки. Но \r
или \r\n
являются частью строки:// trueПродолжение следует…
"A\r\nB".Contains("\n", Ordinal);
// false
"A\r\nB".Contains("\n", InvariantCulture);
//true
"A\r\nB".Contains("\r", InvariantCulture);
Источник: https://www.meziantou.net/string-comparisons-are-harder-than-it-seems.htm
👍1
День девятьсот девяносто пятый. #ЗаметкиНаПолях
Сравнение Строк Сложнее, Чем Кажется. Продолжение
Начало
NLS и ICU
В прошлом API глобализации .NET использовали разные базовые библиотеки на разных платформах. В Unix использовались международные компоненты для Unicode (ICU), а в Windows использовалась поддержка национальных языков (NLS). Это привело к некоторым различиям в поведении нескольких API глобализации при запуске приложений на разных платформах. В .NET 5.0 поведение среды выполнения изменилось. API глобализации теперь по умолчанию используют ICU на всех поддерживаемых платформах. Это позволяет приложениям избегать различий между платформами.
Обратите внимание, что ICU также используется в Windows. DLL-библиотеки ICU поставляются с Windows, начиная с Windows 10 версии 1703. Многие приложения уже начали использовать ICU в Windows. Таким образом, переход на использование ICU происходит повсюду, а не только в .NET.
.NET Framework 1.0-4.8: NLS в Windows
NET Core 1.0-3.1: NLS в Windows, ICU в Linux
.NET 5.0+: ICU* в Windows, ICU в Linux
* Можно принудительно использовать NLS с помощью флага System.Globalization.UseNls в файле конфигурации.
Если в версии Windows нет ICU, она автоматически возвращается к NLS. NLS и ICU в некоторых крайних случаях ведут себя по-разному (см. ниже). Кроме того, новая версия библиотеки может вносить изменения в поведение.
Обратите внимание, что переход с NLS на ICU не является критическим изменением. Изменения данных глобализации происходят даже в NLS, и поведение API не следует считать стабильным.
У методов разные режимы сравнения по умолчанию
Вы всегда должны явно указывать компаратор, поскольку значения по умолчанию в разных методах не согласованы. Например,
Анализаторы кода могут обнаруживать места потенциальных ошибок. Чтобы защититься от неожиданного поведения, рекомендуется включить в проекте анализаторы кода Roslyn. Они помогут пометить код, который может непреднамеренно использовать лингвистический компаратор, когда вероятнее всего подразумевался порядковый компаратор. Следующие правила должны помочь выявить эти проблемы:
- CA1307: Specify StringComparison for clarity (для ясности укажите StringComparison)
- CA1309: Use ordinal StringComparison (используйте порядковый режим сравнения)
- CA1310: Specify StringComparison for correctness (укажите StringComparison для корректности)
Эти особые правила не включены по умолчанию. Чтобы включить их и отображать любые нарушения как ошибки сборки, установите следующие свойства в файле проекта:
- https://www.meziantou.net/string-comparisons-are-harder-than-it-seems.htm
- https://docs.microsoft.com/en-us/dotnet/standard/base-types/string-comparison-net-5-plus
Сравнение Строк Сложнее, Чем Кажется. Продолжение
Начало
NLS и ICU
В прошлом API глобализации .NET использовали разные базовые библиотеки на разных платформах. В Unix использовались международные компоненты для Unicode (ICU), а в Windows использовалась поддержка национальных языков (NLS). Это привело к некоторым различиям в поведении нескольких API глобализации при запуске приложений на разных платформах. В .NET 5.0 поведение среды выполнения изменилось. API глобализации теперь по умолчанию используют ICU на всех поддерживаемых платформах. Это позволяет приложениям избегать различий между платформами.
Обратите внимание, что ICU также используется в Windows. DLL-библиотеки ICU поставляются с Windows, начиная с Windows 10 версии 1703. Многие приложения уже начали использовать ICU в Windows. Таким образом, переход на использование ICU происходит повсюду, а не только в .NET.
.NET Framework 1.0-4.8: NLS в Windows
NET Core 1.0-3.1: NLS в Windows, ICU в Linux
.NET 5.0+: ICU* в Windows, ICU в Linux
* Можно принудительно использовать NLS с помощью флага System.Globalization.UseNls в файле конфигурации.
Если в версии Windows нет ICU, она автоматически возвращается к NLS. NLS и ICU в некоторых крайних случаях ведут себя по-разному (см. ниже). Кроме того, новая версия библиотеки может вносить изменения в поведение.
Обратите внимание, что переход с NLS на ICU не является критическим изменением. Изменения данных глобализации происходят даже в NLS, и поведение API не следует считать стабильным.
У методов разные режимы сравнения по умолчанию
Вы всегда должны явно указывать компаратор, поскольку значения по умолчанию в разных методах не согласованы. Например,
string.IndexOf
использует текущий язык и региональные параметры, тогда как string.Equals
использует Ordinal
. Поэтому всегда используйте перегрузку метода с StringComparison
, IEqualityComparer<string>
или IComparer<string>
, когда это возможно:// true, т.к. используется режим сравнения Ordinal
"A\r\nB".Contains("\n");
IndexOf
по причинам, описанным выше, в .NET 5+ выдаёт -1, т.к. используется текущая культура, а до .NET 5 в результате будет 2:"A\r\nB".IndexOf("\n");Это также относится к
Equals
и CompareTo
(и всем операторам ==
, <
, >
, и т.п.):// falseA
"encyclopædia".Equals("encyclopaedia");
CompareTo
опять же выдаёт 0 (равны) до .NET 5 и 1 в .NET 5+:"encyclopædia".CompareTo("encyclopaedia");Защита от неожиданного поведения
Анализаторы кода могут обнаруживать места потенциальных ошибок. Чтобы защититься от неожиданного поведения, рекомендуется включить в проекте анализаторы кода Roslyn. Они помогут пометить код, который может непреднамеренно использовать лингвистический компаратор, когда вероятнее всего подразумевался порядковый компаратор. Следующие правила должны помочь выявить эти проблемы:
- CA1307: Specify StringComparison for clarity (для ясности укажите StringComparison)
- CA1309: Use ordinal StringComparison (используйте порядковый режим сравнения)
- CA1310: Specify StringComparison for correctness (укажите StringComparison для корректности)
Эти особые правила не включены по умолчанию. Чтобы включить их и отображать любые нарушения как ошибки сборки, установите следующие свойства в файле проекта:
<PropertyGroup>Источники:
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<WarningsAsErrors>
$(WarningsAsErrors);CA1307;CA1309;CA1310
</WarningsAsErrors>
</PropertyGroup>
- https://www.meziantou.net/string-comparisons-are-harder-than-it-seems.htm
- https://docs.microsoft.com/en-us/dotnet/standard/base-types/string-comparison-net-5-plus
День девятьсот девяносто шестой. #Оффтоп
Противоречивые Мнения Разработчиков ПО
Отличной пятницы вам, господа. И у меня для вас сегодня вот такая тема для обсуждения. Периодически мы сталкиваемся с такими рассуждениями из уст даже уважаемых в сообществе людей. Приведу несколько примеров из видео по ссылке ниже (кроме тех, которые на канале и так широко обсуждались), а в комментариях расскажите, с чем согласны, с чем нет. И предлагайте свои варианты, от которых у вас «пригорает» больше всего.
1. Программисты, которые не пишут код в свободное время для развлечения, никогда не станут такими же хорошими, как те, кто это делает
Даже самые умные и талантливые люди никогда не станут по-настоящему хорошими программистами, если они не будут относиться к этому как к чему-то большему, чем работа. То есть они они делают небольшие проекты на стороне или просто возятся с множеством разных языков и идей в свободное время. Это не значит, что хорошие программисты ничего не делают, кроме программирования, но они делают больше, чем просто пишут код с 9 до 5.
2. Ленивые программисты - лучшие программисты
Ленивый программист чаще находит способы сократить время, затрачиваемое на написание кода. Это в итоге превращается в инструменты и рабочие процессы, которые могут принести пользу другим разработчикам в компании/команде. Такие разработчики могут искать утилиты или писать мелкие скрипты для автоматизации часто выполняемых действий. Ленивому программисту может потребоваться несколько дополнительных часов, чтобы выпустить первую версию продукта, но он сэкономит месяцы в будущем.
3. ИТ специальность делает вас более разносторонним программистом
Формальное образование открывает вам другие возможности мышления и даёт отличный базис. То, что вы написали код лучше, чем бакалавр в ИТ, не значит, что вы лучше него. Он сможет легко наверстать упущенное, а вот наоборот – вряд ли. Наличие квалификации также показывает целеустремлённость, настойчивость и тот факт, что человек сделает всё возможное, чтобы стать лучше.
4. Тесты не нужно писать заранее, а иногда и вовсе не нужно
Разработчики плохо тестируют собственный код. Вот почему обычно есть группы QA. В большинстве случаев код, который мы пишем, переплетается с другим кодом, который нужно тестировать отдельно, поэтому мы в итоге обмазываемся паттернами, чтобы обеспечить тестируемость. Не то чтобы паттерны – это плохо, но иногда они могут добавлять ненужной сложности, и все ради тестирования… что часто всё равно не работает. Чтобы написать исчерпывающий тест, требуется много времени. Часто больше времени, чем мы готовы выделить. И чем более комплексным является тест, тем более хрупким он становится. Если интерфейс объекта, который он тестирует, изменяется, мы вынуждены переписать тест, который больше не срабатывает. А если меняются требования к системе…
5. Клиент не всегда прав
Слишком часто разработчики просто пишут код, который их просят, и не пытаются разобраться в продукте. Роль разработчика в том, чтобы помогать бизнесу выражать свои идеи. При этом разработчик должен быть заинтересован в понимании бизнеса, чтобы он мог предоставить наилучший возможный результат. И это подразумевает, что иногда владелец продукта будет просить сделать «революционную новинку», а разработчику придётся либо согласиться с этим фактом, либо объяснять наиболее вероятную причину, почему это невозможно. Это взаимовыгодно, потому что владелец продукта понимает, как его мысли воплощаются в продукте, а команда разработчиков понимает, что они делают нечто большее, чем просто пишут код.
6. Не страшно чего-то не знать. Но тебя уволят, если ты даже не сможешь это нагуглить
Интернет (и гугл в частности) - это инструмент. И эффективное его использование – это один из важных навыков, которым должен обладать разработчик.
Источник: https://youtu.be/goy4lZfDtCE
Противоречивые Мнения Разработчиков ПО
Отличной пятницы вам, господа. И у меня для вас сегодня вот такая тема для обсуждения. Периодически мы сталкиваемся с такими рассуждениями из уст даже уважаемых в сообществе людей. Приведу несколько примеров из видео по ссылке ниже (кроме тех, которые на канале и так широко обсуждались), а в комментариях расскажите, с чем согласны, с чем нет. И предлагайте свои варианты, от которых у вас «пригорает» больше всего.
1. Программисты, которые не пишут код в свободное время для развлечения, никогда не станут такими же хорошими, как те, кто это делает
Даже самые умные и талантливые люди никогда не станут по-настоящему хорошими программистами, если они не будут относиться к этому как к чему-то большему, чем работа. То есть они они делают небольшие проекты на стороне или просто возятся с множеством разных языков и идей в свободное время. Это не значит, что хорошие программисты ничего не делают, кроме программирования, но они делают больше, чем просто пишут код с 9 до 5.
2. Ленивые программисты - лучшие программисты
Ленивый программист чаще находит способы сократить время, затрачиваемое на написание кода. Это в итоге превращается в инструменты и рабочие процессы, которые могут принести пользу другим разработчикам в компании/команде. Такие разработчики могут искать утилиты или писать мелкие скрипты для автоматизации часто выполняемых действий. Ленивому программисту может потребоваться несколько дополнительных часов, чтобы выпустить первую версию продукта, но он сэкономит месяцы в будущем.
3. ИТ специальность делает вас более разносторонним программистом
Формальное образование открывает вам другие возможности мышления и даёт отличный базис. То, что вы написали код лучше, чем бакалавр в ИТ, не значит, что вы лучше него. Он сможет легко наверстать упущенное, а вот наоборот – вряд ли. Наличие квалификации также показывает целеустремлённость, настойчивость и тот факт, что человек сделает всё возможное, чтобы стать лучше.
4. Тесты не нужно писать заранее, а иногда и вовсе не нужно
Разработчики плохо тестируют собственный код. Вот почему обычно есть группы QA. В большинстве случаев код, который мы пишем, переплетается с другим кодом, который нужно тестировать отдельно, поэтому мы в итоге обмазываемся паттернами, чтобы обеспечить тестируемость. Не то чтобы паттерны – это плохо, но иногда они могут добавлять ненужной сложности, и все ради тестирования… что часто всё равно не работает. Чтобы написать исчерпывающий тест, требуется много времени. Часто больше времени, чем мы готовы выделить. И чем более комплексным является тест, тем более хрупким он становится. Если интерфейс объекта, который он тестирует, изменяется, мы вынуждены переписать тест, который больше не срабатывает. А если меняются требования к системе…
5. Клиент не всегда прав
Слишком часто разработчики просто пишут код, который их просят, и не пытаются разобраться в продукте. Роль разработчика в том, чтобы помогать бизнесу выражать свои идеи. При этом разработчик должен быть заинтересован в понимании бизнеса, чтобы он мог предоставить наилучший возможный результат. И это подразумевает, что иногда владелец продукта будет просить сделать «революционную новинку», а разработчику придётся либо согласиться с этим фактом, либо объяснять наиболее вероятную причину, почему это невозможно. Это взаимовыгодно, потому что владелец продукта понимает, как его мысли воплощаются в продукте, а команда разработчиков понимает, что они делают нечто большее, чем просто пишут код.
6. Не страшно чего-то не знать. Но тебя уволят, если ты даже не сможешь это нагуглить
Интернет (и гугл в частности) - это инструмент. И эффективное его использование – это один из важных навыков, которым должен обладать разработчик.
Источник: https://youtu.be/goy4lZfDtCE
👍1
День девятьсот девяносто седьмой. #ПолезныйКод
Загрузка сертификата SSL/TLS в .NET
Недавно мне потребовалось настроить оповещение, когда срок действия сертификата подходит к концу или когда он использует небезопасный алгоритм подписи. Первый шаг - получить сертификат. В .NET для этого вы можете использовать класс
Источник: https://www.meziantou.net/downloading-a-ssl-certificate-in-dotnet.htm
Загрузка сертификата SSL/TLS в .NET
Недавно мне потребовалось настроить оповещение, когда срок действия сертификата подходит к концу или когда он использует небезопасный алгоритм подписи. Первый шаг - получить сертификат. В .NET для этого вы можете использовать класс
SslStream
.static class CertificateDownloaderИспользование:
{
//Этот метод обратного вызова нужен для проверки безопасности сертификата
//Но здесь нам не нужна проверка, мы только получаем данные сертификата
private static readonly
RemoteCertificateValidationCallback
сallback = (_, _, _, _) => true;
public static async Task<X509Certificate2?>
GetCertificateAsync(string domain, int port = 443)
{
using var client = new TcpClient(domain, port);
using var sslStream =
new SslStream(client.GetStream(), true, сallback);
// Инициируем соединение, чтобы скачать сертификат
await sslStream
.AuthenticateAsClientAsync(domain)
.ConfigureAwait(false);
var cert = sslStream.RemoteCertificate;
if (cert != null)
return new X509Certificate2(cert);
return null;
}
}
static async Task Main()Далее можно настроить повторяющуюся задачу в Azure Pipelines или GitHub Actions для ежедневной проверки сертификатов, но это выходит за рамки этого поста.
{
var cert = await
CertificateDownloader.GetCertificateAsync("www.google.com");
Console.WriteLine($"Subject: {cert?.Subject}");
Console.WriteLine($"Issuer: {cert?.Issuer}");
Console.WriteLine($"NotBefore: {cert?.NotBefore}");
Console.WriteLine($"NotAfter: {cert?.NotAfter}");
Console.WriteLine($"Algorithm: {cert?.SignatureAlgorithm.FriendlyName}");
}
Источник: https://www.meziantou.net/downloading-a-ssl-certificate-in-dotnet.htm
День девятьсот девяносто восьмой. #ЗаметкиНаПолях #AsyncTips
Обработка задач при завершении
Задача
Имеется коллекция задач, которые будут использоваться с await; требуется организовать обработку каждой задачи после ее завершения. При этом обработка каждой задачи должна происходить сразу же после завершения, без ожидания других задач.
Допустим, у нас есть некоторый набор однотипных задач. Для простоты возьмём задачу, которая просто ожидает заданное количество секунд:
Решение заключается в добавлении оборачивающего асинхронного метода, который обеспечивает ожидание задачи и обработку ее результата. Этот метод можно записать как асинхронную лямбду:
Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 2.
Обработка задач при завершении
Задача
Имеется коллекция задач, которые будут использоваться с await; требуется организовать обработку каждой задачи после ее завершения. При этом обработка каждой задачи должна происходить сразу же после завершения, без ожидания других задач.
Допустим, у нас есть некоторый набор однотипных задач. Для простоты возьмём задачу, которая просто ожидает заданное количество секунд:
async Task<int> DelayAndReturnAsync(int value)Создадим коллекцию этих задач:
{
await Task.Delay(TimeSpan.FromSeconds(value));
return value;
}
Task<int> taskA = DelayAndReturnAsync(2);Можно ожидать каждую задачу в цикле, но тогда они будут обрабатываться последовательно в порядке их нахождения в коллекции:
Task<int> taskB = DelayAndReturnAsync(3);
Task<int> taskC = DelayAndReturnAsync(1);
Task<int>[] tasks = new[] { taskA, taskB, taskC };
// Выводит "2", "3" и "1" вместо "1", "2" и "3".Решение
async Task ProcessTasksAsync(Task<int>[] tasks)
{
foreach (Task<int> task in tasks)
{
var result = await task;
Trace.WriteLine(result);
}
}
Решение заключается в добавлении оборачивающего асинхронного метода, который обеспечивает ожидание задачи и обработку ее результата. Этот метод можно записать как асинхронную лямбду:
async Task ProcessTasksAsync(Task<int>[] tasks)В этом решении обработка задач выполняется конкурентно, тогда как в исходном коде задачи обрабатывались по очереди. Обычно это не создаёт затруднений, но, если такой способ обработки в вашей ситуации недопустим, рассмотрите возможность использования блокировок.
{
Task[] processing = tasks.Select(async t =>
{
var result = await t;
Trace.WriteLine(result);
}).ToArray();
// Ожидаем завершения всей обработки
await Task.WhenAll(processing);
}
Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 2.
День девятьсот девяносто девятый. #ЧтоНовенького
Hot Reload Убрали Отовсюду, Кроме Visual Studio 2022… Но Потом Вернули
Суть
В прошлую среду Microsoft объявили о последних подготовках к выходу .NET 6 (кстати, презентация пройдёт на .NET Conf 2021, начало 9 ноября в 19:00 по Москве). Отдельным большим постом было объявление о релизе Hot Reload, из которого стало известно, что широко разрекламированную утилиту внезапно решили выпилить отовсюду, кроме Visual Studio 2022. То есть, она не работала бы ни в VS Code, ни в Rider, ни в консольных утилитах вроде
Развитие драмы
Решение было быстро замечено и раскритиковано чуть менее, чем всем .NET сообществом. Вот например тред от Microsoft MVP Шона Киллина. Основной посыл критиков в том, что такое решение подрывает доверие ко всей экосистеме. Более того, этот поступок был практически бессмысленным, т.к. весь код утилиты был открыт, и ничто не мешало энтузиастам взять его и запилить собственные версии. А JetBrains уже в пятницу объявили об обновлении Rider, поддерживающем Hot Reload.
В результате в субботу в Microsoft, что называется, «дали заднюю». Директор по управлению программами .NET Скотт Хантер принёс извинения сообществу и сообщил, что пулл реквест по возвращению Hot Reload в .NET SDK будет удовлетворён.
Что это было?
Да чёрт его знает. Мотивы поступка менеджеров Microsoft до сих пор не ясны. Стимулировать использование новой VS 2022? По слухам Microsoft особой прибыли от IDE не получают. Основной поток идёт от Azure. Да и вряд ли это стало бы решающим фактором при выборе рабочего процесса.
В общем очередная победа гражданского общества над корпорацией зла… Хотя, может на это и был изначальный расчёт – нагнать хайпа перед релизом .NET 6?
Источник: https://devblogs.microsoft.com/dotnet/net-hot-reload-support-via-cli/
Hot Reload Убрали Отовсюду, Кроме Visual Studio 2022… Но Потом Вернули
It’s been 0 days since .NET had drama.Такое сообщение выдаёт недавно созданная шуточная утилита dotnet-drama, которую успели написать за выходные.
Суть
В прошлую среду Microsoft объявили о последних подготовках к выходу .NET 6 (кстати, презентация пройдёт на .NET Conf 2021, начало 9 ноября в 19:00 по Москве). Отдельным большим постом было объявление о релизе Hot Reload, из которого стало известно, что широко разрекламированную утилиту внезапно решили выпилить отовсюду, кроме Visual Studio 2022. То есть, она не работала бы ни в VS Code, ни в Rider, ни в консольных утилитах вроде
dotnet watch
. Причём, поначалу было не ясно, почему было принято такое решение. Но, судя по объяснениям от Microsoft (а точнее по их отсутствию), скоро стало понятно, что решение было не техническим, а чисто менеджерским. То есть, готовую рабочую утилиту, которой многие уже начали пользоваться даже в режиме превью, решили убрать… «просто потому что».Развитие драмы
Решение было быстро замечено и раскритиковано чуть менее, чем всем .NET сообществом. Вот например тред от Microsoft MVP Шона Киллина. Основной посыл критиков в том, что такое решение подрывает доверие ко всей экосистеме. Более того, этот поступок был практически бессмысленным, т.к. весь код утилиты был открыт, и ничто не мешало энтузиастам взять его и запилить собственные версии. А JetBrains уже в пятницу объявили об обновлении Rider, поддерживающем Hot Reload.
В результате в субботу в Microsoft, что называется, «дали заднюю». Директор по управлению программами .NET Скотт Хантер принёс извинения сообществу и сообщил, что пулл реквест по возвращению Hot Reload в .NET SDK будет удовлетворён.
Что это было?
Да чёрт его знает. Мотивы поступка менеджеров Microsoft до сих пор не ясны. Стимулировать использование новой VS 2022? По слухам Microsoft особой прибыли от IDE не получают. Основной поток идёт от Azure. Да и вряд ли это стало бы решающим фактором при выборе рабочего процесса.
В общем очередная победа гражданского общества над корпорацией зла… Хотя, может на это и был изначальный расчёт – нагнать хайпа перед релизом .NET 6?
Источник: https://devblogs.microsoft.com/dotnet/net-hot-reload-support-via-cli/
День 1000.
И вот так незаметно прошла 1000 дней. Кстати, мне порядком надоело писать числа прописью, тем более что теперь это будет совсем длинно. Поэтому дальше будут цифры.
К юбилею вот подогнали книжку, над переводом которой работал, поэтому вот хвастаюсь. Купить можно тут.
Спасибо, что читаете. Отдельное спасибо за комментарии и обсуждения в чате.
Может, пора уже какой-нибудь мерч выпустить, что скажете? Давайте для пробы начнём с кружек. Напишите в комментариях, кто хочет кружку. Среди желающих выберу случайным образом несколько человек и отправлю им сувениры. Только сразу оговорюсь, будьте готовы сообщить мне реальные ФИО и адреса, куда прислать.
И вот так незаметно прошла 1000 дней. Кстати, мне порядком надоело писать числа прописью, тем более что теперь это будет совсем длинно. Поэтому дальше будут цифры.
К юбилею вот подогнали книжку, над переводом которой работал, поэтому вот хвастаюсь. Купить можно тут.
Спасибо, что читаете. Отдельное спасибо за комментарии и обсуждения в чате.
Может, пора уже какой-нибудь мерч выпустить, что скажете? Давайте для пробы начнём с кружек. Напишите в комментариях, кто хочет кружку. Среди желающих выберу случайным образом несколько человек и отправлю им сувениры. Только сразу оговорюсь, будьте готовы сообщить мне реальные ФИО и адреса, куда прислать.
День 1002. #ProjectManagement
Почему Плохое ПО Случается с Хорошими Людьми. Начало
Плохое ПО - одна из немногих вещей, которую нельзя решить с помощью денег. У огромных авиакомпаний приложения для поиска рейсов часто уступают студенческим проектам. Корпоративные ИТ-системы - это монстры с огромными бюджетами, создаваемые в течение многих лет. Как бы то ни было, причина плохого ПО не в недостатке финансирования.
Удивительно, но основная причина плохого ПО связана с тем, как управляются проекты разработки. Худшие программные проекты часто создаются схожим образом.
Владельцы проекта хотят создать конкретное решение, никогда явно не определяя проблему, которую они пытаются решить. Они собирают длинный список требований от большой группы заинтересованных сторон. Список передаётся большой группе внешних разработчиков, которая создаёт требуемое ПО с нуля. Когда все требования выполнены, все празднуют окончание проекта.
Однако, проблемы обнаруживаются, когда система попадает в руки реальных пользователей. Она медленная, запутанная и наполнена трудноуловимыми ошибками, из-за которых её использование становится неудобным. К сожалению, к этому времени внешняя группа разработчиков распущена, и некому вносить необходимые исправления. А люди, поддерживающие систему, зачастую не знают, что вызвало эти проблемы изначально, поэтому не могут их эффективно решить, и всё можно начинать заново.
Есть особенности ПО, которые позволяют добиваться успеха даже с ограниченным бюджетом:
1. Повторное использование хорошего ПО позволяет быстро создавать хорошие вещи.
2. Полезность ПО ограничивается не количеством ресурсов, вложенных в его создание, а тем, насколько сложным оно может стать до того, как сломается.
3. Основная ценность ПО - это не написанный код, а знания, накопленные людьми, которые его создали.
1. Повторное использование ПО
ПО легко копировать как на уровне кода, так и на уровне модулей. Современное ПО почти никогда не создается с нуля. Даже самые инновационные приложения создаются с использованием существующего ПО, которое было объединено и модифицировано для достижения нового результата.
Самый большой источник повторно используемых модулей кода - это сообщество с открытым исходным кодом. Использование открытого исходного кода не только ускоряет разработку приложений, но и дает вам доступ к технологиям, которые намного сложнее, чем всё, что вы могли бы разработать самостоятельно. Наиболее популярный открытый код также более безопасен, поскольку множество людей находят и исправляют уязвимости.
Появление облачных сервисов ещё больше расширило возможности повторного использования. Сайты, сетевые сервисы, виртуальные машины, базы данных – всё это можно легко получить всего лишь за абонентскую плату. Облачные сервисы позволяют разработчикам получать выгоду от специализации: поставщик услуг занимается настройкой, обслуживанием и непрерывной разработкой надежного высококачественного ПО, которое используется всеми его подписчиками. Это позволяет разработчикам перестать тратить время на решенные проблемы и вместо этого сосредоточиться на предоставлении реальной ценности для своей компании.
Вы не сможете добиться технического прогресса, если всё своё время будете тратить на воссоздание существующих технологий. Программная инженерия - это создание автоматизированных систем, и одна из первых вещей, которые автоматизируются, - это рутинная работа. Суть в том, чтобы понять, какие системы следует использовать повторно, как настроить их в соответствии с вашими уникальными требованиями и устранить проблемы, обнаруженные в процессе настройки.
Окончание следует...
Источник: https://www.csc.gov.sg/articles/how-to-build-good-software
Почему Плохое ПО Случается с Хорошими Людьми. Начало
Плохое ПО - одна из немногих вещей, которую нельзя решить с помощью денег. У огромных авиакомпаний приложения для поиска рейсов часто уступают студенческим проектам. Корпоративные ИТ-системы - это монстры с огромными бюджетами, создаваемые в течение многих лет. Как бы то ни было, причина плохого ПО не в недостатке финансирования.
Удивительно, но основная причина плохого ПО связана с тем, как управляются проекты разработки. Худшие программные проекты часто создаются схожим образом.
Владельцы проекта хотят создать конкретное решение, никогда явно не определяя проблему, которую они пытаются решить. Они собирают длинный список требований от большой группы заинтересованных сторон. Список передаётся большой группе внешних разработчиков, которая создаёт требуемое ПО с нуля. Когда все требования выполнены, все празднуют окончание проекта.
Однако, проблемы обнаруживаются, когда система попадает в руки реальных пользователей. Она медленная, запутанная и наполнена трудноуловимыми ошибками, из-за которых её использование становится неудобным. К сожалению, к этому времени внешняя группа разработчиков распущена, и некому вносить необходимые исправления. А люди, поддерживающие систему, зачастую не знают, что вызвало эти проблемы изначально, поэтому не могут их эффективно решить, и всё можно начинать заново.
Есть особенности ПО, которые позволяют добиваться успеха даже с ограниченным бюджетом:
1. Повторное использование хорошего ПО позволяет быстро создавать хорошие вещи.
2. Полезность ПО ограничивается не количеством ресурсов, вложенных в его создание, а тем, насколько сложным оно может стать до того, как сломается.
3. Основная ценность ПО - это не написанный код, а знания, накопленные людьми, которые его создали.
1. Повторное использование ПО
ПО легко копировать как на уровне кода, так и на уровне модулей. Современное ПО почти никогда не создается с нуля. Даже самые инновационные приложения создаются с использованием существующего ПО, которое было объединено и модифицировано для достижения нового результата.
Самый большой источник повторно используемых модулей кода - это сообщество с открытым исходным кодом. Использование открытого исходного кода не только ускоряет разработку приложений, но и дает вам доступ к технологиям, которые намного сложнее, чем всё, что вы могли бы разработать самостоятельно. Наиболее популярный открытый код также более безопасен, поскольку множество людей находят и исправляют уязвимости.
Появление облачных сервисов ещё больше расширило возможности повторного использования. Сайты, сетевые сервисы, виртуальные машины, базы данных – всё это можно легко получить всего лишь за абонентскую плату. Облачные сервисы позволяют разработчикам получать выгоду от специализации: поставщик услуг занимается настройкой, обслуживанием и непрерывной разработкой надежного высококачественного ПО, которое используется всеми его подписчиками. Это позволяет разработчикам перестать тратить время на решенные проблемы и вместо этого сосредоточиться на предоставлении реальной ценности для своей компании.
Вы не сможете добиться технического прогресса, если всё своё время будете тратить на воссоздание существующих технологий. Программная инженерия - это создание автоматизированных систем, и одна из первых вещей, которые автоматизируются, - это рутинная работа. Суть в том, чтобы понять, какие системы следует использовать повторно, как настроить их в соответствии с вашими уникальными требованиями и устранить проблемы, обнаруженные в процессе настройки.
Окончание следует...
Источник: https://www.csc.gov.sg/articles/how-to-build-good-software
День 1003. #ProjectManagement
Почему Плохое ПО Случается с Хорошими Людьми. Окончание
Начало
2. ПО ограничено сложностью
Полезность ПО обычно ограничивается его сложностью, а не объемом ресурсов, вложенных в его создание.
ИТ-системы часто полны функций, но пользователи по-прежнему ненавидят их из-за того, насколько запутанными они становятся. Напротив, лучшие мобильные приложения, как правило, хвалят за простоту и интуитивность. Научиться пользоваться программой сложно. А новые функции только ухудшают положение пользователей - сказывается накопленная сложность. Для пользователя удобство не в том, сколько функций может быть реализовано в программе, а в том, что может поместиться в простой интуитивно понятный интерфейс.
Но и инженерный прогресс замедляется, когда проект становится слишком сложным. Каждая новая строка кода, добавляемая в приложение, может взаимодействовать с любой другой строкой. Чем больше кодовая база, тем больше ошибок появляется при создании новой функции. В конце концов, объём работы по внедрению новой функции нивелируется объёмом работы по исправлению ошибок. Это известно как «технический долг» и является основной проблемой при разработке профессионального ПО. По этой причине во многих крупных ИТ-системах есть проблемы, которые не решаются годами.
В таких случаях единственный путь вперед - сделать шаг назад: рационализировать и упростить кодовую базу. Изменить архитектуру, чтобы ограничить неожиданные взаимодействия, удалить некритические функции, использовать автоматизированные инструменты для проверки ошибок.
Создание хорошего ПО предполагает чередование циклов расширения и уменьшения сложности. По мере появления новых функций в системе естественным образом накапливается беспорядок. Когда этот беспорядок начинает вызывать проблемы, прогресс приостанавливается, чтобы дать время на очистку. Этот двухэтапный процесс необходим, потому что идеально хорошей инженерии не существует. Задача состоит в том, чтобы управлять этим циклом, позволяя ПО стать достаточно беспорядочным, чтобы добиться значимого прогресса, но не позволяя ему становиться настолько сложным, что с ним невозможно справиться.
3. ПО - это не только написание кода, но и накопление знаний.
В разработке ПО большинство идей плохие. Их количество настолько велико, что любая конкретная идея, вероятно, не сработает, даже если она была тщательно продумана и отобрана. Чтобы добиться прогресса, нужно начать с множества плохих идей, отбросить худшие и развить самые многообещающие. Конечный продукт может быть обманчиво простым, но знание того, почему было выбрано именно это решение, позволяет ему быть хорошим.
Эти знания остаются важными даже после создания продукта. Если новая команда возьмёт на себя код незнакомого ПО, оно вскоре начнёт деградировать. Появятся новые требования, обнаружатся ошибки в безопасности, и все эти проблемы новая команда, не имея глубоких знаний архитектуры системы и принципов проектирования, скорее всего будет решать временными заплатками. Из-за этого появятся новые ошибки, а сложность системы в целом возрастёт. К ПО следует относиться не как к статичному продукту, а как к живому проявлению коллективного понимания проблемы командой разработчиков.
Вот почему сложно полагаться на аутсорс при разработке ключевого для компании ПО. Вы можете получить работающую систему и её код, но бесценные знания о том, как она построена и какие варианты дизайна были выбраны, покинут организацию. Поэтому передача системы новым людям для «обслуживания» часто вызывает проблемы. Даже если система очень хорошо документирована, некоторые знания теряются каждый раз, когда приходит новая команда. С годами система превращается в лоскутное одеяло из кода разных авторов. В конце концов, не остаётся никого, кто действительно понимал бы, как всё это работает. Чтобы ПО продолжало хорошо работать в долгосрочной перспективе, обслуживающий персонал должен работать и учиться вместе с аутсорс командой, чтобы сохранить важные инженерные знания внутри вашей организации.
Источник: https://www.csc.gov.sg/articles/how-to-build-good-software
Почему Плохое ПО Случается с Хорошими Людьми. Окончание
Начало
2. ПО ограничено сложностью
Полезность ПО обычно ограничивается его сложностью, а не объемом ресурсов, вложенных в его создание.
ИТ-системы часто полны функций, но пользователи по-прежнему ненавидят их из-за того, насколько запутанными они становятся. Напротив, лучшие мобильные приложения, как правило, хвалят за простоту и интуитивность. Научиться пользоваться программой сложно. А новые функции только ухудшают положение пользователей - сказывается накопленная сложность. Для пользователя удобство не в том, сколько функций может быть реализовано в программе, а в том, что может поместиться в простой интуитивно понятный интерфейс.
Но и инженерный прогресс замедляется, когда проект становится слишком сложным. Каждая новая строка кода, добавляемая в приложение, может взаимодействовать с любой другой строкой. Чем больше кодовая база, тем больше ошибок появляется при создании новой функции. В конце концов, объём работы по внедрению новой функции нивелируется объёмом работы по исправлению ошибок. Это известно как «технический долг» и является основной проблемой при разработке профессионального ПО. По этой причине во многих крупных ИТ-системах есть проблемы, которые не решаются годами.
В таких случаях единственный путь вперед - сделать шаг назад: рационализировать и упростить кодовую базу. Изменить архитектуру, чтобы ограничить неожиданные взаимодействия, удалить некритические функции, использовать автоматизированные инструменты для проверки ошибок.
Создание хорошего ПО предполагает чередование циклов расширения и уменьшения сложности. По мере появления новых функций в системе естественным образом накапливается беспорядок. Когда этот беспорядок начинает вызывать проблемы, прогресс приостанавливается, чтобы дать время на очистку. Этот двухэтапный процесс необходим, потому что идеально хорошей инженерии не существует. Задача состоит в том, чтобы управлять этим циклом, позволяя ПО стать достаточно беспорядочным, чтобы добиться значимого прогресса, но не позволяя ему становиться настолько сложным, что с ним невозможно справиться.
3. ПО - это не только написание кода, но и накопление знаний.
В разработке ПО большинство идей плохие. Их количество настолько велико, что любая конкретная идея, вероятно, не сработает, даже если она была тщательно продумана и отобрана. Чтобы добиться прогресса, нужно начать с множества плохих идей, отбросить худшие и развить самые многообещающие. Конечный продукт может быть обманчиво простым, но знание того, почему было выбрано именно это решение, позволяет ему быть хорошим.
Эти знания остаются важными даже после создания продукта. Если новая команда возьмёт на себя код незнакомого ПО, оно вскоре начнёт деградировать. Появятся новые требования, обнаружатся ошибки в безопасности, и все эти проблемы новая команда, не имея глубоких знаний архитектуры системы и принципов проектирования, скорее всего будет решать временными заплатками. Из-за этого появятся новые ошибки, а сложность системы в целом возрастёт. К ПО следует относиться не как к статичному продукту, а как к живому проявлению коллективного понимания проблемы командой разработчиков.
Вот почему сложно полагаться на аутсорс при разработке ключевого для компании ПО. Вы можете получить работающую систему и её код, но бесценные знания о том, как она построена и какие варианты дизайна были выбраны, покинут организацию. Поэтому передача системы новым людям для «обслуживания» часто вызывает проблемы. Даже если система очень хорошо документирована, некоторые знания теряются каждый раз, когда приходит новая команда. С годами система превращается в лоскутное одеяло из кода разных авторов. В конце концов, не остаётся никого, кто действительно понимал бы, как всё это работает. Чтобы ПО продолжало хорошо работать в долгосрочной перспективе, обслуживающий персонал должен работать и учиться вместе с аутсорс командой, чтобы сохранить важные инженерные знания внутри вашей организации.
Источник: https://www.csc.gov.sg/articles/how-to-build-good-software
День 1004. #NetBasics
Чем Отличается SDK от Runtime?
Официальная страница .NET предлагает 2 опции для скачивания:
- SDK (Software Development Kit) – комплект для разработки ПО со средой исполнения,
- Runtime – только среду исполнения.
Среда исполнения — это то, что должно быть установлено на машине конечного пользователя или сервере для запуска приложения .NET. SDK - это набор инструментов для разработки ПО, который позволяет программистам разрабатывать приложения .NET. Проще говоря, SDK — это то, что создаёт исполняемые файлы, а среда исполнения — это то, что может их запускать.
Разработчикам ПО нужно скачивать .NET SDK, чтобы создавать приложения .NET, но веб-сервер или компьютер конечного пользователя могут иметь только среду исполнения .NET, которая гораздо меньше по размеру.
Ещё один момент — это слабая взаимосвязь между разными языками .NET. Как видно на картинке ниже, C#, F# и VB.NET находятся на разных этапах своей жизни и, следовательно, имеют разные версии. Языки могут развиваться независимо и вводить новые языковые функции по своему усмотрению до тех пор, пока связанный компилятор переводит исходный код в действительный IL.
Внимательный наблюдатель мог бы также заметить несоответствие между версией .NET, .NET SDK и среды исполнения .NET. Официальная версия .NET обычно относится к версии среды исполнения .NET, потому что это, по сути, последняя среда исполнения, которую необходимо установить на машине. SDK может иметь другую версию, потому что цепочка инструментов разработки может улучшаться быстрее, чем сама среда исполнения, и поддерживать новые функции и улучшенные процессы разработки, по-прежнему ориентируясь на ту же версию .NET.
Для справки: не для каждого языка программирования требуется SDK и среда исполнения. Например, такие языки, как Rust или Go, которые напрямую компилируются в собственный машинный код, не требуют среды исполнения. Для этих языков доступен только один вариант загрузки, который обычно представляет собой SDK для создания ПО. .NET же похож, например, на Java, в которой также есть JDK (Java Development Kit) для создания программного обеспечения и JRE (Java Runtime Environment) для исполнения кода.
Источник: https://dusted.codes/dotnet-basics
Чем Отличается SDK от Runtime?
Официальная страница .NET предлагает 2 опции для скачивания:
- SDK (Software Development Kit) – комплект для разработки ПО со средой исполнения,
- Runtime – только среду исполнения.
Среда исполнения — это то, что должно быть установлено на машине конечного пользователя или сервере для запуска приложения .NET. SDK - это набор инструментов для разработки ПО, который позволяет программистам разрабатывать приложения .NET. Проще говоря, SDK — это то, что создаёт исполняемые файлы, а среда исполнения — это то, что может их запускать.
Разработчикам ПО нужно скачивать .NET SDK, чтобы создавать приложения .NET, но веб-сервер или компьютер конечного пользователя могут иметь только среду исполнения .NET, которая гораздо меньше по размеру.
Ещё один момент — это слабая взаимосвязь между разными языками .NET. Как видно на картинке ниже, C#, F# и VB.NET находятся на разных этапах своей жизни и, следовательно, имеют разные версии. Языки могут развиваться независимо и вводить новые языковые функции по своему усмотрению до тех пор, пока связанный компилятор переводит исходный код в действительный IL.
Внимательный наблюдатель мог бы также заметить несоответствие между версией .NET, .NET SDK и среды исполнения .NET. Официальная версия .NET обычно относится к версии среды исполнения .NET, потому что это, по сути, последняя среда исполнения, которую необходимо установить на машине. SDK может иметь другую версию, потому что цепочка инструментов разработки может улучшаться быстрее, чем сама среда исполнения, и поддерживать новые функции и улучшенные процессы разработки, по-прежнему ориентируясь на ту же версию .NET.
Для справки: не для каждого языка программирования требуется SDK и среда исполнения. Например, такие языки, как Rust или Go, которые напрямую компилируются в собственный машинный код, не требуют среды исполнения. Для этих языков доступен только один вариант загрузки, который обычно представляет собой SDK для создания ПО. .NET же похож, например, на Java, в которой также есть JDK (Java Development Kit) для создания программного обеспечения и JRE (Java Runtime Environment) для исполнения кода.
Источник: https://dusted.codes/dotnet-basics