День семьсот восемнадцатый. #ЗаметкиНаПолях
Делаем Заглавными Первые Буквы Слов
Работа над презентацией – значительная часть разработки ПО. Мы тратим на форматирование строк больше времени, чем хотелось бы.
Регистр заголовка - это использование первой заглавной буквы в каждом слове в строке, оставляя остальные буквы строчными. Конечно, можно это сделать вручную, но для этого в .NET есть специальный объект.
Большинство элементов отображения текста не универсальны для .NET, а управляются экземплярами типа
Остаётся одна проблема. По правилам английского написания слово «is» не должно начинаться с заглавной буквы. Аналогично в других языках предлоги и союзы должны оставаться строчными. Например:
Немецкий
Исходная строка: Per anhalter durch die galaxis
Ожидаемый результат: Per Anhalter durch die Galaxis
Реальный результат: Per Anhalter Durch Die Galaxis
Французский
Исходная строка: les naufragés d'ythaq
Ожидаемый результат: Les Naufragés d'Ythaq
Реальный результат: Les Naufragés D'ythaq
Как показано выше, метод
Источник: https://khalidabuhakmeh.com/capitalize-first-letter-of-words-with-csharp
Делаем Заглавными Первые Буквы Слов
Работа над презентацией – значительная часть разработки ПО. Мы тратим на форматирование строк больше времени, чем хотелось бы.
Регистр заголовка - это использование первой заглавной буквы в каждом слове в строке, оставляя остальные буквы строчными. Конечно, можно это сделать вручную, но для этого в .NET есть специальный объект.
Большинство элементов отображения текста не универсальны для .NET, а управляются экземплярами типа
CultureInfo
, хранящими настройки языка и региональных параметров. Экземпляры CultureInfo
определяют стили даты, валюты и т.п. В CultureInfo
также вложен класс TextInfo
, который определяет систему написания слов в культуре. Его метод ToTitleCase
можно использовать для преобразования текста в регистр заголовка.using static System.Console;Заметьте, что аббревиатура .NET оставлена в изначальном виде, тогда как прописные буквы в слове "aWESome" заменены строчными.
using static System.Globalization.CultureInfo;
var сulture = GetCultureInfo("en-gb");
var info = сulture.TextInfo;
var name = ".NET is aWESome";
WriteLine(info.ToTitleCase(name));
//Вывод: .NET Is Awesome
Остаётся одна проблема. По правилам английского написания слово «is» не должно начинаться с заглавной буквы. Аналогично в других языках предлоги и союзы должны оставаться строчными. Например:
Немецкий
Исходная строка: Per anhalter durch die galaxis
Ожидаемый результат: Per Anhalter durch die Galaxis
Реальный результат: Per Anhalter Durch Die Galaxis
Французский
Исходная строка: les naufragés d'ythaq
Ожидаемый результат: Les Naufragés d'Ythaq
Реальный результат: Les Naufragés D'ythaq
Как показано выше, метод
ToTitleCase
выдаёт результат, который не обязательно является лингвистически правильным. Лингвистически правильное решение потребует дополнительных правил, а текущий алгоритм проще и быстрее. Однако в Microsoft не исключают добавление этих правил в будущем, вследствие чего алгоритм может стать медленнее, а также не будет гарантироваться одинаковая длина исходного текста и результата.Источник: https://khalidabuhakmeh.com/capitalize-first-letter-of-words-with-csharp
День семьсот девятнадцатый. #ЧтоНовенького
Более интегрированный терминал
В Visual Studio 2019 v16.8 добавили несколько новых фишек во встроенный терминал.
Интеграция с Обозревателем Решений
Новая команда контекстного меню позволяет открыть терминал по определенному пути. Выберите решение, проект или папку, нажмите правой кнопкой мыши и выберите Open in Terminal («Открыть в терминале»). См. картинку 1.
Команды Панели Инструментов
На панели инструментов окна терминала добавлены кнопки для копирования и вставки, а также кнопка настроек. См. картинку 2. Во многих терминалах
Источник: https://devblogs.microsoft.com/visualstudio/a-more-integrated-terminal-experience/
Более интегрированный терминал
В Visual Studio 2019 v16.8 добавили несколько новых фишек во встроенный терминал.
Интеграция с Обозревателем Решений
Новая команда контекстного меню позволяет открыть терминал по определенному пути. Выберите решение, проект или папку, нажмите правой кнопкой мыши и выберите Open in Terminal («Открыть в терминале»). См. картинку 1.
Команды Панели Инструментов
На панели инструментов окна терминала добавлены кнопки для копирования и вставки, а также кнопка настроек. См. картинку 2. Во многих терминалах
Ctrl+C
используется для прерывания текущей операции. Поэтому по умолчанию для копирования и вставки используются Ctrl+Shift+C
и Ctrl+Shift+V
. Однако их можно настроить. Перейдите в Options > Environment > Keyboard (Опции > Настройки среды > Клавиатура) и отредактируйте горячие клавиши для команд Terminal.Copy и Terminal.Paste.Источник: https://devblogs.microsoft.com/visualstudio/a-more-integrated-terminal-experience/
День семьсот двадцатый. #Оффтоп #97Вещей
97 Вещей, Которые Должен Знать Каждый Программист
74. Принцип Единственной Ответственности
Вспомним SOLID. Один из основных принципов хорошего дизайна ПО гласит:
Соберите вместе то, что меняется по одной и той же причине, и разделите то, что меняется по разным причинам.
Этот принцип часто называют принципом единственной ответственности или SRP. Более строго, в нём говорится, что подсистема, модуль, класс или даже функция не должны иметь более одной причины для изменения. Классическим примером является класс, в котором есть методы, которые работают с бизнес-правилами, отчётами и базой данных:
Хороший дизайн системы означает, что мы разделяем систему на компоненты, которые можно развернуть независимо. Независимое развёртывание означает, что, если мы изменим один компонент, нам не придется повторно развёртывать какие-либо другие. Однако, если Employee активно используется многими другими классами в других компонентах, то каждое изменение в
Внимательный читатель увидит, что в приведенном выше решении всё ещё есть зависимости. Классы
Тщательное применение SRP, разделение вещей, которые меняются по разным причинам, является одним из ключей к созданию проектов, которые имеют независимо развёртываемую структуру компонентов.
Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
Автор оригинала – Robert C. Martin
97 Вещей, Которые Должен Знать Каждый Программист
74. Принцип Единственной Ответственности
Вспомним SOLID. Один из основных принципов хорошего дизайна ПО гласит:
Соберите вместе то, что меняется по одной и той же причине, и разделите то, что меняется по разным причинам.
Этот принцип часто называют принципом единственной ответственности или SRP. Более строго, в нём говорится, что подсистема, модуль, класс или даже функция не должны иметь более одной причины для изменения. Классическим примером является класс, в котором есть методы, которые работают с бизнес-правилами, отчётами и базой данных:
public class Employee {На первый взгляд объединение этих трёх методов в одном классе совершенно уместно. В конце концов, классы и должны быть наборами функций, которые работают с общими данными. Однако проблема в том, что эти три метода меняются по совершенно разным причинам. Метод
public double CalculatePay() …
public string ReportHours() …
public void Save() …
}
CalculatePay
будет меняться всякий раз, когда меняются бизнес-правила расчёта оплаты. Метод ReportHours
будет меняться, когда кто-то захочет изменить формат отчёта. Метод Save
будет изменяться всякий раз, когда администраторы баз данных изменяют схему базы данных. Эти три причины для изменений в совокупности делают класс Employee очень нестабильным. Он будет изменяться по любой из этих причин. Что ещё более важно, эти изменения повлияют на любые классы, которые зависят от Employee.Хороший дизайн системы означает, что мы разделяем систему на компоненты, которые можно развернуть независимо. Независимое развёртывание означает, что, если мы изменим один компонент, нам не придется повторно развёртывать какие-либо другие. Однако, если Employee активно используется многими другими классами в других компонентах, то каждое изменение в
Employee
, вероятно, приведет к повторному развёртыванию других компонентов, тем самым сводя на нет главное преимущество компонентного дизайна. Следующее простое разделение решает эту проблему:public class Employee {Каждый класс можно поместить в отдельный компонент. Точнее, скорее всего, все классы отчётности войдут в компонент отчётов. Все классы, связанные с базой данных, - в компонент репозитория. А все бизнес-правила могут быть включены в компонент бизнес-правил.
public double CalculatePay()…
}
public class EmployeeReporter {
public string ReportHours(Employee e)…
}
public class EmployeeRepository {
public void Save(Employee e)…
}
Внимательный читатель увидит, что в приведенном выше решении всё ещё есть зависимости. Классы
EmployeeReporter
и EmployeeRepository
по-прежнему зависят от Employee
. Поэтому, если Employee
будет изменён, их, вероятно, придётся перекомпилировать и повторно развернуть. Таким образом, Employee всё ещё нельзя изменить, а затем развернуть независимо. Однако другие классы можно. Никакая модификация в EmployeeReporter
или EmployeeRepository
не может привести к необходимости перекомпиляции или повторного развёртывания других классов. И даже Employee
можно было бы развернуть независимо за счет осторожного использования принципа инверсии зависимостей.Тщательное применение SRP, разделение вещей, которые меняются по разным причинам, является одним из ключей к созданию проектов, которые имеют независимо развёртываемую структуру компонентов.
Источник: https://www.oreilly.com/library/view/97-things-every/9780596809515/
Автор оригинала – Robert C. Martin
День семьсот двадцать первый. #TypesAndLanguages
2. Исключение при Обработке Исключения
В прошлый раз мы рассмотрели, что не стоит использовать
Результат различается в разных языках. Например, C# теряет исключение, что указано в спецификации:
Если блок finally выбрасывает другое исключение, обработка текущего исключения прекращается.
Стоит отметить, что некоторые языки предоставляют поле в классе исключения, которое должно хранить предыдущее. Но, если внутреннее исключение не устанавливается автоматически платформой, то это поле нам никак не поможет.
Это важно при работе с ресурсами. Некоторые языки предоставляют конструкцию типа
Источники:
- https://blog.adamfurmanek.pl/2021/01/16/types-and-programming-languages-part-2/
- https://blog.adamfurmanek.pl/2020/07/25/net-inside-out-part-21/
2. Исключение при Обработке Исключения
В прошлый раз мы рассмотрели, что не стоит использовать
return
в блоке finally
. Сегодня исследуем аналогичный случай исключения при обработке исключения:try{Что будет выведено? Этот вопрос с подвохом. Во-первых, есть два исключения, и мы знаем, что некоторые языки (включая платформу .NET) реализуют двухпроходную систему обработки исключений. Первый проход по стеку ищет обработчик, способный обработать исключение, затем второй проход раскручивает стек и выполняет все блоки
try{
throw new Exception("Exception 1");
}finally{
throw new Exception("Exception 2");
}
}catch(Exception e){
Console.WriteLine(e);
}
finally
. Но что, если мы выбросим исключение во втором проходе?Результат различается в разных языках. Например, C# теряет исключение, что указано в спецификации:
Если блок finally выбрасывает другое исключение, обработка текущего исключения прекращается.
Стоит отметить, что некоторые языки предоставляют поле в классе исключения, которое должно хранить предыдущее. Но, если внутреннее исключение не устанавливается автоматически платформой, то это поле нам никак не поможет.
Это важно при работе с ресурсами. Некоторые языки предоставляют конструкцию типа
using
в C#:using(var resource = new Resource()){Концептуально это аналогично следующему коду:
//…
}
var resource = new Resource();Кажется довольно просто. Но что если
try{
//…
} finally{
if(resource != null) resource.Dispose();
}
Dispose
выбросит исключение: public class Resource : IDisposable {Тогда следующий код:
public void Dispose(){
Console.WriteLine("Disposing");
throw new Exception("Disposing failed");
}
}
try{выведет
using(var resource = new Resource()){
Console.WriteLine("Using");
throw new Exception("Using failed");
}
}catch(Exception e){
Console.WriteLine("Exception: " + e);
}
UsingМы теряем исключение "Using failed" из-за нового исключения "Disposing failed" в блоке finally. Кстати, в Java это реализовано правильно, и сохраняет оба исключения.
Disposing
Exception: System.Exception: Disposing failed
at Resource.Dispose()
at Program.Main()
Источники:
- https://blog.adamfurmanek.pl/2021/01/16/types-and-programming-languages-part-2/
- https://blog.adamfurmanek.pl/2020/07/25/net-inside-out-part-21/
День семьсот двадцать второй. #Оффтоп
Вытащу несколько вещей из чата. Во-первых, чтобы самому не забыть, а во-вторых, для тех, кто не следит за чатом. Сегодня вещь первая.
Интересное видео – очередная серия подкаста Solo on .NET от Дмитрия Нестерука о языках программирования. Хотел написать, что подойдёт новичкам для выбора языка. Но потом вспомнил количество технологий и узкоспециализированных терминов, о которых говорит Дмитрий, и понял, что новичок послушает, ужаснётся и вообще откажется от затеи связать свою жизнь с программированием)))
В общем, не совсем для новичков будут интересны рассуждения о выборе языка: если вы не знаете, о чём это, и боитесь спросить. Для совсем не новичков просто сравнительный анализ языков и технологий, а также немного «за жизнь». Мне понравилось.
Вытащу несколько вещей из чата. Во-первых, чтобы самому не забыть, а во-вторых, для тех, кто не следит за чатом. Сегодня вещь первая.
Интересное видео – очередная серия подкаста Solo on .NET от Дмитрия Нестерука о языках программирования. Хотел написать, что подойдёт новичкам для выбора языка. Но потом вспомнил количество технологий и узкоспециализированных терминов, о которых говорит Дмитрий, и понял, что новичок послушает, ужаснётся и вообще откажется от затеи связать свою жизнь с программированием)))
В общем, не совсем для новичков будут интересны рассуждения о выборе языка: если вы не знаете, о чём это, и боитесь спросить. Для совсем не новичков просто сравнительный анализ языков и технологий, а также немного «за жизнь». Мне понравилось.
День семьсот двадцать третий. #Оффтоп
Вытащу несколько вещей из чата. Во-первых, чтобы самому не забыть, а во-вторых, для тех, кто не следит за чатом. Сегодня вещь вторая. См. вещь первая.
Разговор зашёл о вопросах на собеседовании и, в частности, про TAP (Task-based Asynchronous Pattern), про которую очень любят спрашивать, например, в Epam. Поскольку тема обширная и запутанная, или, цитируя один из источников ниже: «При попытках понять асинхронность наблюдается чередование состояний "теперь понятно" и "теперь снова непонятно".», - в этом посте сохраню несколько источников для изучения темы.
1. Книга «Конкурентность в C#» Стивена Клири
Как обычно, выкладываю ссылку только на магазин, кто захочет нахаляву, тот найдёт. Сам я её пока не читал, но те, кто читал, рекомендуют. Однако, она не для изучения темы с нуля. Поэтому для начала можно почитать следующее.
2. «The Task-based Asynchronous Pattern» Stephen Toub, Microsoft, 2012
Статья около 35 страниц. Довольно старая, но концептуально ничего не поменялось.
3. «Async/await в C#: концепция, внутреннее устройство, полезные приемы»
Полезная статья, возможно немного сумбурная, потому что сжато. Мне показалось, что это вольный пересказ 5й главы книги «С# In Depth» Джона Скита. Для любителей читать в печатном варианте, см. вложение ниже.
4. Также скромно порекомендую полистать посты на канале, например, по тегу #AsyncAwaitFAQ.
Вытащу несколько вещей из чата. Во-первых, чтобы самому не забыть, а во-вторых, для тех, кто не следит за чатом. Сегодня вещь вторая. См. вещь первая.
Разговор зашёл о вопросах на собеседовании и, в частности, про TAP (Task-based Asynchronous Pattern), про которую очень любят спрашивать, например, в Epam. Поскольку тема обширная и запутанная, или, цитируя один из источников ниже: «При попытках понять асинхронность наблюдается чередование состояний "теперь понятно" и "теперь снова непонятно".», - в этом посте сохраню несколько источников для изучения темы.
1. Книга «Конкурентность в C#» Стивена Клири
Как обычно, выкладываю ссылку только на магазин, кто захочет нахаляву, тот найдёт. Сам я её пока не читал, но те, кто читал, рекомендуют. Однако, она не для изучения темы с нуля. Поэтому для начала можно почитать следующее.
2. «The Task-based Asynchronous Pattern» Stephen Toub, Microsoft, 2012
Статья около 35 страниц. Довольно старая, но концептуально ничего не поменялось.
3. «Async/await в C#: концепция, внутреннее устройство, полезные приемы»
Полезная статья, возможно немного сумбурная, потому что сжато. Мне показалось, что это вольный пересказ 5й главы книги «С# In Depth» Джона Скита. Для любителей читать в печатном варианте, см. вложение ниже.
4. Также скромно порекомендую полистать посты на канале, например, по тегу #AsyncAwaitFAQ.
День семьсот двадцать четвёртый. #MoreEffectiveCSharp
20. Учитывайте, Что События Усиливают Связанность Объектов во Время Выполнения
Кажется, что события предоставляют способ полностью отделить ваш класс от классов, которые он должен уведомлять. Вы определяете тип события и позволяете подписываться на него. Внутри вашего класса вы вызываете событие. Ваш класс ничего не знает о подписчиках и не налагает ограничений на них. Код можно расширить, создавая любое необходимое поведение при возникновении этих событий. Однако не всё так просто.
Начнём с того, что некоторые типы аргументов событий содержат флаги состояния, которые предписывают вашему классу выполнять определённые операции.
Во время выполнения возникает ещё одна форма связи между источником события и подписчиками. Источник содержит ссылку на делегат, который предоставляет подписчик. Время жизни объекта подписчика теперь будет соответствовать времени жизни объекта источника. Источник будет вызывать обработчик подписчика всякий раз, когда происходит событие. Но это не должно продолжаться после удаления подписчика. То есть подписчикам на события необходимо реализовать паттерн Disposable и отписываться от события в методе Dispose(). В противном случае подписчики продолжат существовать, поскольку в источнике будут ссылки на их делегаты.
Это еще один случай, когда связывание во время выполнения может вам дорого обойтись несмотря на то, что связь кажется более слабой. Связь на основе событий ослабляет статическую связь между типами, но за счет более тесной связи между источником события и подписчиками во время выполнения.
Вы должны учесть эти проблемы в своем дизайне, чтобы использовать события.
Источник: Bill Wagner “More Effective C#”. – 2nd ed. Глава 20.
20. Учитывайте, Что События Усиливают Связанность Объектов во Время Выполнения
Кажется, что события предоставляют способ полностью отделить ваш класс от классов, которые он должен уведомлять. Вы определяете тип события и позволяете подписываться на него. Внутри вашего класса вы вызываете событие. Ваш класс ничего не знает о подписчиках и не налагает ограничений на них. Код можно расширить, создавая любое необходимое поведение при возникновении этих событий. Однако не всё так просто.
Начнём с того, что некоторые типы аргументов событий содержат флаги состояния, которые предписывают вашему классу выполнять определённые операции.
public class WorkerEngine {Теперь все подписчики на это событие связаны. Если один подписчик запросит отмену операции, установив Cancel в true, другой может отменить это. Таким образом, последний подписчик в цепочке может переопределить действие любого предыдущего. Невозможно заставить иметь только одного подписчика, и нет способа гарантировать, что делегат какого-то из подписчиков будет выполнен последним. Вы можете изменить аргументы события, чтобы гарантировать, что после установки флага отмены ни один подписчик не сможет его выключить:
public event
EventHandler<WorkerEventArgs> OnProgress;
public void DoLotsOfStuff() {
for (int i = 0; i < 100; i++) {
SomeWork();
var args = new WorkerEventArgs();
args.Percent = i;
OnProgress?.Invoke(this, args);
if (args.Cancel) return;
}
}
private void SomeWork(){…}
}
public class WorkerEventArgs : EventArgs {Это изменение сработает в этом случае, но так можно сделать не всегда. Если вам нужно убедиться, что есть ровно один подписчик, придётся выбрать другой способ связи классов. Например, определить интерфейс и вызывать метод интерфейса вместо события. Или запрашивать делегат подписчика в качестве параметра метода. Затем этот единственный подписчик может решить, хочет ли он поддерживать несколько подписчиков и как организовать семантику запросов на отмену.
public int Percent { get; set; }
public bool Cancel { get; private set; }
public void RequestCancel() {
Cancel = true;
}
}
Во время выполнения возникает ещё одна форма связи между источником события и подписчиками. Источник содержит ссылку на делегат, который предоставляет подписчик. Время жизни объекта подписчика теперь будет соответствовать времени жизни объекта источника. Источник будет вызывать обработчик подписчика всякий раз, когда происходит событие. Но это не должно продолжаться после удаления подписчика. То есть подписчикам на события необходимо реализовать паттерн Disposable и отписываться от события в методе Dispose(). В противном случае подписчики продолжат существовать, поскольку в источнике будут ссылки на их делегаты.
Это еще один случай, когда связывание во время выполнения может вам дорого обойтись несмотря на то, что связь кажется более слабой. Связь на основе событий ослабляет статическую связь между типами, но за счет более тесной связи между источником события и подписчиками во время выполнения.
Вы должны учесть эти проблемы в своем дизайне, чтобы использовать события.
Источник: Bill Wagner “More Effective C#”. – 2nd ed. Глава 20.
День семьсот двадцать пятый. #Оффтоп
Вы Всё Ещё не Пишете Тесты?
Редкий пост с моими собственными мыслями, без источника. Мысли предельно примитивные, поэтому не ругайте строго.
Я всегда ненавидел писать тесты. Думаю, я далеко не одинок в этом. И на работе у нас не то, чтобы это обязательно (да, я чувствую ваше осуждение). Поэтому тесты пишутся по остаточному принципу, когда есть время (а его почти никогда нет). Кроме того, некоторые читатели моего канала знают, что мне приходится поддерживать ещё классический ASP, а там с тестами примерно никак (хотя, есть мною с нуля написанная система тестов основных функций). Поэтому и практики особой у меня в этом деле не было.
Но недавние события заставили меня взглянуть на тесты совсем по-другому. Нет, тут не будет ничего нового для более-менее опытных разработчиков. Думаю, это смогли ощутить на себе почти все, кто писал тесты на свой (или чужой) код. Просто личная история, подтверждение теории практикой.
1. Тесты помогают найти ошибки
У нас на сайте есть общедоступная статистика. Некоторые отчёты доступны года с 2003го, другие появились позже. Есть несколько предустановленных периодов, которые пользователь может выбрать для удобства, чтобы не устанавливать даты вручную: предыдущая неделя, предыдущий месяц, квартал, год, год-до-текущей-даты и т.п. Собственно, написан метод (не мной), которому передаётся выбранный период (например, предыдущий месяц) и сроки доступности отчёта, а выдаётся дата начала и дата окончания периода. Однако период передаётся в виде 2х параметров: тип периода (день, неделя, месяц, квартал, год) и количество периодов. То есть используется метод всегда с количеством 1 (предыдущий), но теоретически можно передавать и другое число. Кроме того, результирующие даты ограничиваются сроком доступности отчёта. Например, если запрошен отчёт за предыдущие 2 года, а доступен он только за год, то дата начала берётся как первый день доступности отчёта. Аналогично с датой окончания.
Получил тикет, что неверно выбирается предыдущий квартал. Метод написан осенью, а сейчас вылезло, что 4й квартал 2020го начинается не с октября, а с сентября. Простейшая опечатка. В логике поставлен не 10й месяц, а 9й. Остальная логика на первый взгляд казалась правильной, но какой-то странной. Поэтому я решил всё-таки написать серию тестов. Так вот, поскольку метод использовался с количеством периодов равным 1, он чуть ли не в половине случаев неверно рассчитывал даты, если задать другое количество периодов. Я в итоге нашёл 3 или 4 ошибки в расчётах. Даже в такой вроде бы не самой сложной логике!
2. Покрытие тестами реально повышает уверенность в коде
У вас бывало так, что перед релизом вроде всё работает, но всё равно терзает какое-то странное чувство, что что-то не так? Тесты позволяют избавиться от этого чувства. В методе, который я описал выше, я, конечно, изменил не только 9й месяц на 10й. Я исправил примерно четверть его кода. Но из-за наличия тестов я на 100 (хорошо, на 99) процентов уверен, что теперь он работает правильно.
Вы Всё Ещё не Пишете Тесты?
Редкий пост с моими собственными мыслями, без источника. Мысли предельно примитивные, поэтому не ругайте строго.
Я всегда ненавидел писать тесты. Думаю, я далеко не одинок в этом. И на работе у нас не то, чтобы это обязательно (да, я чувствую ваше осуждение). Поэтому тесты пишутся по остаточному принципу, когда есть время (а его почти никогда нет). Кроме того, некоторые читатели моего канала знают, что мне приходится поддерживать ещё классический ASP, а там с тестами примерно никак (хотя, есть мною с нуля написанная система тестов основных функций). Поэтому и практики особой у меня в этом деле не было.
Но недавние события заставили меня взглянуть на тесты совсем по-другому. Нет, тут не будет ничего нового для более-менее опытных разработчиков. Думаю, это смогли ощутить на себе почти все, кто писал тесты на свой (или чужой) код. Просто личная история, подтверждение теории практикой.
1. Тесты помогают найти ошибки
У нас на сайте есть общедоступная статистика. Некоторые отчёты доступны года с 2003го, другие появились позже. Есть несколько предустановленных периодов, которые пользователь может выбрать для удобства, чтобы не устанавливать даты вручную: предыдущая неделя, предыдущий месяц, квартал, год, год-до-текущей-даты и т.п. Собственно, написан метод (не мной), которому передаётся выбранный период (например, предыдущий месяц) и сроки доступности отчёта, а выдаётся дата начала и дата окончания периода. Однако период передаётся в виде 2х параметров: тип периода (день, неделя, месяц, квартал, год) и количество периодов. То есть используется метод всегда с количеством 1 (предыдущий), но теоретически можно передавать и другое число. Кроме того, результирующие даты ограничиваются сроком доступности отчёта. Например, если запрошен отчёт за предыдущие 2 года, а доступен он только за год, то дата начала берётся как первый день доступности отчёта. Аналогично с датой окончания.
Получил тикет, что неверно выбирается предыдущий квартал. Метод написан осенью, а сейчас вылезло, что 4й квартал 2020го начинается не с октября, а с сентября. Простейшая опечатка. В логике поставлен не 10й месяц, а 9й. Остальная логика на первый взгляд казалась правильной, но какой-то странной. Поэтому я решил всё-таки написать серию тестов. Так вот, поскольку метод использовался с количеством периодов равным 1, он чуть ли не в половине случаев неверно рассчитывал даты, если задать другое количество периодов. Я в итоге нашёл 3 или 4 ошибки в расчётах. Даже в такой вроде бы не самой сложной логике!
2. Покрытие тестами реально повышает уверенность в коде
У вас бывало так, что перед релизом вроде всё работает, но всё равно терзает какое-то странное чувство, что что-то не так? Тесты позволяют избавиться от этого чувства. В методе, который я описал выше, я, конечно, изменил не только 9й месяц на 10й. Я исправил примерно четверть его кода. Но из-за наличия тестов я на 100 (хорошо, на 99) процентов уверен, что теперь он работает правильно.
День семьсот двадцать шестой. #ЗаметкиНаПолях
Как Работает Git. Начало
Определение Git в Википедии - распределённая система контроля версий. То есть мало того, что Git делает то, что делают другие системы контроля версий (такие как Subversion), он делает это распределённым способом.
Это довольно сложно понять сразу, поэтому уберём один слой – распределённость. Представим, что в мире есть только один компьютер и на нём есть репозиторий. Таким образом, Git становится просто системой контроля версий. Однако это тоже сложное понятие. Оно включает в себя такие вещи, как история, ветки, слияния… Упрощаем дальше. Забудем про ветки, историю и т.п. Теперь это можно назвать примитивным трекером контента (stupid content tracker – определение Git из документации). Потому что это всё, что он делает - отслеживает контент, файлы или каталоги. Но давайте забудем и об отслеживании контента. Посмотрим на самую суть, основную идею. И на самом деле Git – это просто карта, которая сопоставляет ключи со значениями.
Значения - это просто любая последовательность байт, например, содержимое текстового или даже двоичного файла. Для ключа Git вычисляет хэш значения с помощью алгоритма SHA‑1. У каждого объекта в репозитории есть собственный хэш-ключ. SHA‑1 представляет собой последовательность из 40 шестнадцатеричных цифр, например,
Хранение
Теперь посмотрите в папку с любым репозиторием Git. В ней есть скрытая папка
И точно так же хранятся все коммиты. Коммит – это просто текстовый файл, хранящийся в репозитории. Его так же можно посмотреть через
Продолжение следует…
Источник: https://app.pluralsight.com/library/courses/how-git-works
Как Работает Git. Начало
Определение Git в Википедии - распределённая система контроля версий. То есть мало того, что Git делает то, что делают другие системы контроля версий (такие как Subversion), он делает это распределённым способом.
Это довольно сложно понять сразу, поэтому уберём один слой – распределённость. Представим, что в мире есть только один компьютер и на нём есть репозиторий. Таким образом, Git становится просто системой контроля версий. Однако это тоже сложное понятие. Оно включает в себя такие вещи, как история, ветки, слияния… Упрощаем дальше. Забудем про ветки, историю и т.п. Теперь это можно назвать примитивным трекером контента (stupid content tracker – определение Git из документации). Потому что это всё, что он делает - отслеживает контент, файлы или каталоги. Но давайте забудем и об отслеживании контента. Посмотрим на самую суть, основную идею. И на самом деле Git – это просто карта, которая сопоставляет ключи со значениями.
Значения - это просто любая последовательность байт, например, содержимое текстового или даже двоичного файла. Для ключа Git вычисляет хэш значения с помощью алгоритма SHA‑1. У каждого объекта в репозитории есть собственный хэш-ключ. SHA‑1 представляет собой последовательность из 40 шестнадцатеричных цифр, например,
239982efa3b9bb5cf84e1b8a9f188025432e789e
. Для получения хэша значения используется команда git hash-object
.Хранение
Теперь посмотрите в папку с любым репозиторием Git. В ней есть скрытая папка
.git
, которая и хранит репозиторий. Зайдите в папку .git/objects
. Пока не обращайте внимания на папки init
и pack
. Заметьте, что остальные папки содержат первые 2 символа хэша, а файлы внутри этих папок названы оставшимися 38 символами. Для объекта выше это будет папка 23
, файл 9982efa3b9bb5cf84e1b8a9f188025432e789e
. Этот файл содержит сжатое значение. Значение можно посмотреть с помощью команды git cat-file
, передав ей хэш. Параметр -t
выдаст нам тип объекта (в данном случае blob
), а параметр -p
распакует и выдаст содержимое файла.И точно так же хранятся все коммиты. Коммит – это просто текстовый файл, хранящийся в репозитории. Его так же можно посмотреть через
git cat-file
. Только, если передать параметр -t
, то результатом будет commit
. git cat-file 1177e23 -pПомимо данных об авторе, времени и сообщении лога, коммит содержит хэш ещё одного объекта типа tree. В Git
tree be4d42cd6ecc95e9fa5bc3e029bf28bb263104c0
author S.Benzenko <…> 1606106642 +0300
committer S.Benzenko <…> 1606106642 +0300
Initial commit
blob
представляет файл, а tree
- папку. Коммит указывает на корень проекта. Так же, как и всё остальное, tree
– это текстовый файл, который содержит список хэшей файлов и папок, их имена, а также цифровое представление прав доступа к ним. git cat-file be4d42cd -pПри этом Git хранит только содержимое файла. Если несколько файлов имеют одинаковое содержимое (т.е. одинаковый хэш), реально храниться в репозитории содержимое будет только один раз в объекте
100644 tree 3ee7d56f… folder1
100644 blob 239982ef… file1.txt
blob
с этим хэшем, но несколько объектов tree
будут ссылаться этот объект blob
и хранить разные имена этих файлов. Например, посмотрим содержимое папки folder1
:git cat-file 3ee7d56 -pЗдесь два файла
100644 blob 361ad437… README.md
100644 blob 239982ef… samefile1.txt
file1.txt
и samefile1.txt
имеют одинаковое содержимое, поэтому хранятся, как один blob
объект с хэшем 239982ef…
. Имена файлов хранятся в объектах tree
be4d42cd
(корень) и 3ee7d56
(папка folder1
).Продолжение следует…
Источник: https://app.pluralsight.com/library/courses/how-git-works
День семьсот двадцать седьмой. #ЗаметкиНаПолях
Как Работает Git. Продолжение
Начало
Версии
Посмотрим, что же из себя представляют версии. Любой коммит, кроме первого, помимо ссылки на tree (хэша) содержит ссылку
Посмотрим на упрощённую объектную модель Git (см. рисунок ниже). Итак, у нас есть структура, в которой одни объекты содержат данные (
В нашем проекте есть файл
Во втором коммите
Продолжение следует...
Источник: https://app.pluralsight.com/library/courses/how-git-works
Как Работает Git. Продолжение
Начало
Версии
Посмотрим, что же из себя представляют версии. Любой коммит, кроме первого, помимо ссылки на tree (хэша) содержит ссылку
parent
– предыдущий коммит.git cat-file 25087999 -pНовый коммит хранит ссылку на предыдущий (
tree 6ee02ade…
parent 1177e235…
author S. Benzenko <…> 1606107663 +0300
committer S. Benzenko <…> 1606107663 +0300
Second commit
1177e23
), а также ссылку на новый корень проекта (tree
) - 6ee02ade
(см. рисунок ниже). Однако, если посмотреть, что хранит новый корень, то там будут ссылки на изменённые в этом коммите файлы, а также ссылка на объект tree из предыдущего коммита, поскольку эти объекты не менялись. Таким образом, каждый коммит содержит только изменения относительно предыдущего. А если вы храните огромный файл, и меняете в нём лишь небольшую часть, Git может выполнить несколько оптимизаций, чтобы не хранить этот большой файл дважды. Вы можете посмотреть размер репозитория с помощью команды git count-objects
.Посмотрим на упрощённую объектную модель Git (см. рисунок ниже). Итак, у нас есть структура, в которой одни объекты содержат данные (
blob
) – аналоги файлов в файловой системе, другие объекты (tree
) – аналоги папок – содержат ссылки на объекты blob
и tree
. Получается древовидная структура, причём имена объектов blob
и tree
не хранятся в самих объектах. Вместо этого они хранятся в содержащем их объекте tree. Таким образом, у вас может быть один и тот же объект blob
или tree
, на который указывают разные объекты tree
, хранящие разные имена. Это очень похоже на файловую систему. Ну и поверх этого наложены объекты commit
, позволяющие управлять версиями.В нашем проекте есть файл
file1.txt
и папка folder1
с файлами samefile1.txt
и README.md
. Файлы file1.txt
и samefile1.txt
имеют одинаковое содержимое. В файловой системе file1.txt
находится в корне, а samefile1.txt
находится в папке folder1
. Однако в Git оба файла ссылаются на один объект blob с хэшем 2399
.Во втором коммите
2508
изменилось только содержимое файла file1.txt
. Поэтому новый корень 6ee0
ссылается на новый объект blob
- f1fe
. Другие файлы не менялись, поэтому корни обоих коммитов ссылаются на один и тот же объект tree
(3ee7
), представляющий собой папку folder1
. Таким образом текущая версия файлов получается простым проходом по ссылкам из коммита. Из коммита 2508
можно «найти» только изменившуюся версию файла file1.txt
(f1fe
), но нельзя «дойти» до версии 2399
(на неё ссылается только файл samefile1.txt
). Аналогично из коммита 1177
нельзя получить доступ к изменённой версии file1.txt
. Однако все объекты внутри папки folder1 доступны из обоих коммитов, поскольку они не изменялись.Продолжение следует...
Источник: https://app.pluralsight.com/library/courses/how-git-works
День семьсот двадцать восьмой. #ЗаметкиНаПолях
Как Работает Git. Продолжение
Начало
Версии
Ветки
Мы еще не создавали веток, но одна у нас уже есть. Это ветка
Кроме этого, при необходимости меняется содержимое файлов в рабочем каталоге. В предыдущем посте мы разбирали диаграмму объектов. Объекты
После смены ветки
Что же происходит, когда мы сливаем ветки (
1. Создаётся новый коммит. Он будет отличаться тем, что у него будет два родительских коммита (2 записи
2. Ссылка текущей ветки перемещается на новый коммит.
3. Ссылка ветки, которую мы слили с текущей, остаётся на её последнем коммите (см. рисунок ниже).
4. Обновляются файлы в рабочей папке.
Два главных вывода:
1. Не бойтесь обилия объектов
2. На самом деле Git не заботится о вашей рабочей папке. При перемещении по истории Git просто заменяет содержимое рабочей папки данными из репозитория. Объекты в репозитории неизменяемы и постоянны, а файлы в вашей рабочей папке временны. Да, Git предупредит при возможной потере изменений в изменённых файлах (например, если попытаться выполнить
Отделённая голова
Помимо перехода на другую ветку, можно перейти и к любому доступному коммиту, например:
Продолжение следует…
Источник: https://app.pluralsight.com/library/courses/how-git-works
Как Работает Git. Продолжение
Начало
Версии
Ветки
Мы еще не создавали веток, но одна у нас уже есть. Это ветка
master
(main
в последних версиях Git). Список веток можно посмотреть с помощью команды git branch
:* mainОбычно ветки размещаются в каталоге
.git/refs/heads
, и действительно, мы видим там файл main
с SHA-1 хэшем, который соответствует последнему коммиту в ветке. То есть ветка – это просто ссылка на коммит. Если у нас несколько веток, все они будут размещены здесь в виде файлов со ссылками. Какая ветка является текущей? Это определяется в файле .git/HEAD
:ref: refs/heads/mainТо есть
HEAD
– это просто ссылка на текущую ветку. Когда мы делаем коммит, файл refs/heads/main
меняется и получает ссылку на новый коммит. Когда мы создаём новую ветку, например:git branch newили переключаемся на существующую ветку,
git checkout new
меняется файл HEAD, он получает ссылку на новую ветку, например, refs/heads/new
. Кроме этого, при необходимости меняется содержимое файлов в рабочем каталоге. В предыдущем посте мы разбирали диаграмму объектов. Объекты
commit
используются для отслеживания истории. Содержимое рабочей папки восстанавливается простым переходом по ссылкам из соответствующего коммита. Все объекты, которые доступны из коммита, восстанавливаются в рабочей папке, остальные удаляются.После смены ветки
git branch
выведет:mainЗдесь звёздочка указывает на текущую ветку (на которую ссылается файл
* new
HEAD
).Что же происходит, когда мы сливаем ветки (
git merge
).1. Создаётся новый коммит. Он будет отличаться тем, что у него будет два родительских коммита (2 записи
parent
, см. предыдущий пост). 2. Ссылка текущей ветки перемещается на новый коммит.
3. Ссылка ветки, которую мы слили с текущей, остаётся на её последнем коммите (см. рисунок ниже).
4. Обновляются файлы в рабочей папке.
Два главных вывода:
1. Не бойтесь обилия объектов
tree
и blob
. Перемещаться по истории в Git довольно просто. Сосредоточьтесь на коммитах, и том как они связаны друг с другом, а Git сам разберётся с объектами репозитория.2. На самом деле Git не заботится о вашей рабочей папке. При перемещении по истории Git просто заменяет содержимое рабочей папки данными из репозитория. Объекты в репозитории неизменяемы и постоянны, а файлы в вашей рабочей папке временны. Да, Git предупредит при возможной потере изменений в изменённых файлах (например, если попытаться выполнить
git checkout
), но не более того.Отделённая голова
Помимо перехода на другую ветку, можно перейти и к любому доступному коммиту, например:
git checkout ecbebe660Эта ситуация называется «отделённая голова» (detached head). Вы можете продолжить работу и добавлять коммиты,
HEAD
будет ссылаться на последний добавленный вами коммит (см. рисунок ниже). Однако, если вы решите перейти на одну из веток, больше ничто не будет ссылаться на коммиты, добавленные в режиме detached head, и рано или поздно они будут удалены сборщиком мусора (да, он тут тоже есть). Чтобы «спасти» их, просто нужно создать новую ветку. Этот приём используется в случаях, когда вы хотите попробовать какие-то изменения, но не уверены, нужно ли сохранять их в репозитории.Продолжение следует…
Источник: https://app.pluralsight.com/library/courses/how-git-works
День семьсот двадцать девятый. #ЗаметкиНаПолях
Как Работает Git. Продолжение
Начало
Версии
Ветки
Rebase
Допустим, мы хотим соединить содержимое двух веток (см. рисунок ниже). Мы можем слить их с помощью
Нельзя просто так взять и перенести коммиты. Объекты репозитория неизменяемы. А если вы измените что-либо в коммите (например, ссылку на родителя), вы получите другой хэш, то есть другой коммит. Поэтому на самом деле создаётся новый синий коммит с тем же содержимым и единственным отличием, что родительским коммитом у него будет не красный, а последний из жёлтых (см. рисунок ниже). Затем копируется следующий синий коммит (потому что его родитель тоже изменился), и так далее. Таким образом, эти новые коммиты выглядят почти так же, как оригинальные, но это новые объекты с новыми хэшами. И, наконец, Git перемещает указатель перебазированной ветки на новые коммиты. Старые коммиты, как мы рассмотрели в предыдущем посте остаются без ветки и со временем удаляются сборщиком мусора.
Merge
+ сохраняет историю в неизменном виде,
+ при возникновении конфликтов слияния получившийся коммит будет включать исправления конфликтов,
- в больших проектах история становится громоздкой и запутанной.
Rebase
+ сохраняет историю чистой и аккуратной,
- эта история не настоящая (например, на рисунке ниже кажется, что жёлтые коммиты созданы раньше, чем синие),
- создают конфликты при распределённой разработке, когда несколько человек работают над проектом параллельно.
Итого: если сомневаетесь, используйте merge. Rebase – мощный и полезный инструмент, но им можно пользоваться, только если вы знаете, что делаете, и понимаете последствия.
Коротко о Тэгах
Тэги – это ещё один тип объектов репозитория. В Git есть два типа тэгов: аннотированные и без аннотаций (или легковесные). Создать тэг можно с помощью команды:
Тэги расположены в каталоге
Продолжение следует…
Источник: https://app.pluralsight.com/library/courses/how-git-works
Как Работает Git. Продолжение
Начало
Версии
Ветки
Rebase
Допустим, мы хотим соединить содержимое двух веток (см. рисунок ниже). Мы можем слить их с помощью
merge
, однако есть другой способ – расположить одну ветку поверх другой (rebase
). В этом случае Git находит общий коммит двух веток (красный на рисунке), а затем берёт коммиты синей ветки и помещает их после коммитов жёлтой ветки. Мы соединили ветки в одну не за счёт слияния в один общий коммит, а за счёт перегруппировки коммитов так, чтобы они выглядели как одна ветка. На самом деле, конечно, не всё так просто.Нельзя просто так взять и перенести коммиты. Объекты репозитория неизменяемы. А если вы измените что-либо в коммите (например, ссылку на родителя), вы получите другой хэш, то есть другой коммит. Поэтому на самом деле создаётся новый синий коммит с тем же содержимым и единственным отличием, что родительским коммитом у него будет не красный, а последний из жёлтых (см. рисунок ниже). Затем копируется следующий синий коммит (потому что его родитель тоже изменился), и так далее. Таким образом, эти новые коммиты выглядят почти так же, как оригинальные, но это новые объекты с новыми хэшами. И, наконец, Git перемещает указатель перебазированной ветки на новые коммиты. Старые коммиты, как мы рассмотрели в предыдущем посте остаются без ветки и со временем удаляются сборщиком мусора.
Merge
+ сохраняет историю в неизменном виде,
+ при возникновении конфликтов слияния получившийся коммит будет включать исправления конфликтов,
- в больших проектах история становится громоздкой и запутанной.
Rebase
+ сохраняет историю чистой и аккуратной,
- эта история не настоящая (например, на рисунке ниже кажется, что жёлтые коммиты созданы раньше, чем синие),
- создают конфликты при распределённой разработке, когда несколько человек работают над проектом параллельно.
Итого: если сомневаетесь, используйте merge. Rebase – мощный и полезный инструмент, но им можно пользоваться, только если вы знаете, что делаете, и понимаете последствия.
Коротко о Тэгах
Тэги – это ещё один тип объектов репозитория. В Git есть два типа тэгов: аннотированные и без аннотаций (или легковесные). Создать тэг можно с помощью команды:
git tag <имя тэга>Для аннотированного тэга надо добавить параметр
‑a
и опционально -m
и описание. Аннотированный тэг будет содержать много полезной информации, такой как дата создания, автор, описание и т.д. Тэг без аннотации имеет только имя.Тэги расположены в каталоге
.git/refs/tags
. Тэг – это такая же ссылка на коммит, как и ветка. На самом деле можно переместить файл тэга в папку .git/refs/heads
, и он «станет веткой» (не делайте так). Есть только одно отличие: при создании нового коммита ссылка в ветке изменяется, чтобы указывать на него, а тэг так и ссылается на тот же самый коммит. Можно перейти к тэгу с помощью git checkout, точно также, как можно перейти к ветке или коммиту. Тэги часто используются для отметки версии продукта в истории коммитов.Продолжение следует…
Источник: https://app.pluralsight.com/library/courses/how-git-works
День семьсот тридцатый. #ЗаметкиНаПолях
Как Работает Git. Окончание
Начало
Версии
Ветки
Rebase
Распределённость
Мы наконец добрались до верхнего слоя определения Git (распределённая система контроля версий). Допустим, мы создали репозиторий на GitHub. Получить копию его на локальном компьютере можно с помощью команды
Теперь есть два клона репозитория, один на GitHub и один на локальном компьютере, и оба клона одинаково хороши. В отличие от Subversion или других традиционных систем контроля версий, в Git нет централизованного сервера. Оба компьютера теперь содержат весь проект и его историю. У нас может быть сколько угодно клонов. Вы можете решить, что один конкретный клон является самым важным. Но это условность, договорённость. Технически все клоны равноправны.
При клонировании репозитория через
Git также требуется сохранять состояние локального и удалённого репозиториев. Для состояния удалённого репозитория используется папка
Иногда из-за внутренних оптимизаций файл
Синхронизация
Команда
Однако, если кто-то другой добавил коммит в удалённый репозиторий, возникает конфликт. Решить его можно двумя способами:
1. Принудительный push
Команда
2. Разрешение конфликта
Сначала выполняется команда
Источник: https://app.pluralsight.com/library/courses/how-git-works
Как Работает Git. Окончание
Начало
Версии
Ветки
Rebase
Распределённость
Мы наконец добрались до верхнего слоя определения Git (распределённая система контроля версий). Допустим, мы создали репозиторий на GitHub. Получить копию его на локальном компьютере можно с помощью команды
git clone <адрес репозитория>В локальную папку будет сохранено содержимое папки
.git
, то есть вся история. В последних версиях, правда, git clone
по умолчанию копирует только ветку main
. После копирования репозитория Git восстанавливает файлы рабочей области из репозитория.Теперь есть два клона репозитория, один на GitHub и один на локальном компьютере, и оба клона одинаково хороши. В отличие от Subversion или других традиционных систем контроля версий, в Git нет централизованного сервера. Оба компьютера теперь содержат весь проект и его историю. У нас может быть сколько угодно клонов. Вы можете решить, что один конкретный клон является самым важным. Но это условность, договорённость. Технически все клоны равноправны.
При клонировании репозитория через
git clone
добавляется несколько строк в конфигурацию нашего репозитория (файл .git/config
):[remote "origin"]Каждый репозиторий Git может запоминать информацию о других копиях, которые называются удалёнными (
url = https://github.com/user/repo
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
remote = origin
merge = refs/heads/main
remote
). Копия по умолчанию называется origin
. В конфигурации сохраняется url удалённого репозитория, а также связь локальной ветки main
с веткой main
из origin
. Вы можете настроить эту конфигурацию, чтобы изменить политику синхронизации.Git также требуется сохранять состояние локального и удалённого репозиториев. Для состояния удалённого репозитория используется папка
.git/refs/remotes/origin
. Здесь мы можем видеть знакомые нам файлы HEAD
и main
, предназначение которых такое же, как и у их локальных копий: main
хранит ссылку на последний коммит, HEAD
– ссылку на текущую ветку.Иногда из-за внутренних оптимизаций файл
main
может отсутствовать, и тогда информация о состоянии репозиториев будет храниться в файле .git/packed-refs
. Посмотреть состояние локального и удалённого репозиториев можно с помощью команды git show-ref
.Синхронизация
Команда
git push
сохраняет локальные изменения и коммиты в удалённый репозиторий. Это просто, когда репозитории изначально были синхронизированы (см. красный коммит в левой части рисунка ниже). Тогда ваши изменения (фиолетовый коммит) будут сохранены, ссылка main в удалённом репозитории и origin/main
в локальном перейдут на новый коммит.Однако, если кто-то другой добавил коммит в удалённый репозиторий, возникает конфликт. Решить его можно двумя способами:
1. Принудительный push
Команда
git push -f
принудит внести ваши изменения в удалённый репозиторий (см. в средней части рисунка ниже). Тогда указатель ветки main
перейдёт на ваш коммит (фиолетовый), а коммит, сделанный другим человеком (зелёный), со временем будет удалён сборщиком мусора, как будто его и не было.2. Разрешение конфликта
Сначала выполняется команда
git fetch
, которая получит данные из удалённого репозитория в ваш локальный (см. в правой части рисунка ниже). Далее командой git merge origin/main
удалённая ветка сливается с вашей локальной в новый коммит (розовый). Это настолько частый случай, что для этих операций есть отдельная команда git pull
. После этого команда git push
сохранит все получившиеся изменения в удалённом репозитории.Источник: https://app.pluralsight.com/library/courses/how-git-works
День семьсот тридцать первый. #Оффтоп #97Вещей
97 Вещей, Которые Должен Знать Каждый Программист
75. Боритесь со Стрессом
Каждый из нас, будь то начинающий или опытный программист, когда-нибудь попадал в ситуацию, когда всё бесит. Задача не решается, мелкий пакостный баг никак не получается отловить, компоненты или фреймворки отказываются работать друг с другом, IDE тормозит, кто-то постоянно отвлекает… Такие большие и мелкие неприятности постепенно выводят из душевного равновесия, и ваша производительность катастрофически снижается. Что сделать, чтобы этого не случалось?
1. Рабочий стол
Приберитесь на рабочем столе (как реальном, так и виртуальном). Уберите отвлекающие вещи, вроде телефона. Поставьте рядом вещи, которые вас успокаивают, с которыми вам уютно. Это может быть фото семьи, ваша детская игрушка, чётки или эспандер, чтобы повертеть в руке. Найдите комфортное положение в кресле. Вам удобнее сидеть прямо или откинувшись на спинку?
2. Внутренний цикл разработчика
Обычный цикл разработчики состоит в повторении трёх шагов:
- написание кода
- запуск
- отладка
Убедитесь, что переход по шагам этого цикла максимально быстр и прост. Как мы пишем веб-сайт? Пишем код, собираем проект, запускаем сайт, обновляем браузер, останавливаем сайт, делаем изменение, собираем проект… При этом каждый раз мы переключаемся между окнами, убираем руку с клавиатуры, чтобы взять мышку и наоборот. Это очень муторно.
Как ускорить этот процесс?
Во-первых, с помощью клавиш
Во-вторых, в .NET есть монитор файлов
3. Навыки редактора
Изучите IDE и горячие клавиши для наиболее часто повторяемых операций. Поищите информацию о том, что вообще умеет IDE (например, см. посты по тэгу #TipsAndTricks).
Эти советы помогут уменьшить количество раздражающих вещей. Но что делать, если вы всё-таки попали в ситуацию, когда задача не решается, непонятно, что делать и всё бесит?
- Сделайте глубокий вдох и откиньтесь на кресле.
- Закройте лишние окна, вкладки в браузере или файлы в IDE.
- Отвлекитесь от монитора и возьмите блокнот и карандаш, запишите план того, что вам нужно сделать.
- Прогуляйтесь.
- Объясните проблему «утёнку».
Главное, запомните, что вам помогло снизить стресс и решить проблему, чтобы использовать этот метод в следующий раз.
Что вам помогает выйти из стрессовой ситуации? Оставляйте комментарии.
Источник: https://www.hanselman.com/blog/the-art-of-rubber-ducking-or-rubber-duck-debugging/
Автор оригинала – Scott Hanselman
97 Вещей, Которые Должен Знать Каждый Программист
75. Боритесь со Стрессом
Каждый из нас, будь то начинающий или опытный программист, когда-нибудь попадал в ситуацию, когда всё бесит. Задача не решается, мелкий пакостный баг никак не получается отловить, компоненты или фреймворки отказываются работать друг с другом, IDE тормозит, кто-то постоянно отвлекает… Такие большие и мелкие неприятности постепенно выводят из душевного равновесия, и ваша производительность катастрофически снижается. Что сделать, чтобы этого не случалось?
1. Рабочий стол
Приберитесь на рабочем столе (как реальном, так и виртуальном). Уберите отвлекающие вещи, вроде телефона. Поставьте рядом вещи, которые вас успокаивают, с которыми вам уютно. Это может быть фото семьи, ваша детская игрушка, чётки или эспандер, чтобы повертеть в руке. Найдите комфортное положение в кресле. Вам удобнее сидеть прямо или откинувшись на спинку?
2. Внутренний цикл разработчика
Обычный цикл разработчики состоит в повторении трёх шагов:
- написание кода
- запуск
- отладка
Убедитесь, что переход по шагам этого цикла максимально быстр и прост. Как мы пишем веб-сайт? Пишем код, собираем проект, запускаем сайт, обновляем браузер, останавливаем сайт, делаем изменение, собираем проект… При этом каждый раз мы переключаемся между окнами, убираем руку с клавиатуры, чтобы взять мышку и наоборот. Это очень муторно.
Как ускорить этот процесс?
Во-первых, с помощью клавиш
Win+стрелки
можно расположить окна на экране так, чтобы не приходилось каждый раз их скрывать и разворачивать. Кстати, а вы знаете, что в Windows можно сделать несколько рабочих столов с независимыми наборами программ в каждом? Нажмите Win+Tab
, и в верхней плашке вы увидите свой рабочий стол и кнопку «Новый рабочий стол» (New desktop). Туда вы можете переместить любые окна, которые вам в данный момент не нужны. Переключаться между рабочими столами можно с помощью Ctrl+Win+стрелки
. Кроме того, существуют программы, позволяющие вам разделить экран на зоны (например, в треть высоты или по четвертям) окна, помещённые в зону, автоматически примут её размер.Во-вторых, в .NET есть монитор файлов
dotnet-watch
. Например, команда dotnet watch run
, выполненная в папке сайта, заставит dotnet
следить за изменениями в папке, и как только вы сохраняете файл, будет пересобирать и перезапускать веб-приложение. Также можно настроить выполнение тестов, выбрать отслеживаемые типы файлов и т.п. И таких мелких утилит тысячи. Мы не задумываемся о таких вещах, погружённые в процесс, автоматически выполняем набор действий. Но иногда полезно последить, можно ли автоматизировать какие-то повторяющиеся процессы.3. Навыки редактора
Изучите IDE и горячие клавиши для наиболее часто повторяемых операций. Поищите информацию о том, что вообще умеет IDE (например, см. посты по тэгу #TipsAndTricks).
Эти советы помогут уменьшить количество раздражающих вещей. Но что делать, если вы всё-таки попали в ситуацию, когда задача не решается, непонятно, что делать и всё бесит?
- Сделайте глубокий вдох и откиньтесь на кресле.
- Закройте лишние окна, вкладки в браузере или файлы в IDE.
- Отвлекитесь от монитора и возьмите блокнот и карандаш, запишите план того, что вам нужно сделать.
- Прогуляйтесь.
- Объясните проблему «утёнку».
Главное, запомните, что вам помогло снизить стресс и решить проблему, чтобы использовать этот метод в следующий раз.
Что вам помогает выйти из стрессовой ситуации? Оставляйте комментарии.
Источник: https://www.hanselman.com/blog/the-art-of-rubber-ducking-or-rubber-duck-debugging/
Автор оригинала – Scott Hanselman
День семьсот тридцать второй. #ЧтоНовенького
Планы на EF Core 6.0
EF Core 6.0 является следующим релизом после EF Core 5.0 и в настоящее время запланирован на ноябрь 2021 года одновременно с .NET 6. EF Core 6.0 будет соответствовать .NET 6 в качестве выпуска с долгосрочной поддержкой (LTS). Он не будет работать в .NET Framework и маловероятно, что будет поддерживать какую-либо версию .NET Standard. Приведённые ниже планы – это пока лишь предложения от сообщества, и они не обязательно будут реализованы. Кроме того, может появиться и что-то другое.
Самые востребованные функции
1. Темпоральные таблицы SQL Server
Создание темпоральных таблиц с помощью миграции, а также доступ к историческим данным с помощью запросов LINQ.
2. Столбцы типа JSON
Общие шаблоны для поддержки JSON, которые могут быть реализованы любым поставщиком базы данных. Поддержка столбцов JSON будет реализована для SQL Server и SQLite (PostgreSQL и MySQL уже их поддерживают).
3. ColumnAttribute.Order
Произвольный порядок столбцов при создании таблицы с помощью Migrations или EnsureCreated.
Производительность
1. Инфраструктура производительности и новые тесты
Улучшение инфраструктуры для тестов производительности, а также добавление новых тестов и исправление ошибок.
2. Скомпилированные модели
Скомпилированные модели улучшат производительность запуска, а также в целом улучшат производительность при доступе к модели.
3. TechEmpower Fortunes
Планируется сравниться по производительности Dapper на тесте TechEmpower Fortunes.
4. Компоновщики/AOT
Продолжатся исследования по улучшению работы EF Core с компоновщиками и AOT.
Миграции и развертывание
1. Пакеты миграций
Пакеты миграции предоставят простой и надежный механизм для развертывания миграций EF Core.
2. Управление миграциями
Планируется улучшить инструменты и управление проектами/сборками для миграций EF Core.
Кроме того, планируется улучшение существующих функций и исправление известных ошибок. Полный план на EF Core 6.0 можно найти здесь. Также вы можете голосовать за новые функции или оставлять свои предложения в GitHub.
Источник: https://devblogs.microsoft.com/dotnet/the-plan-for-entity-framework-core-6-0/
Планы на EF Core 6.0
EF Core 6.0 является следующим релизом после EF Core 5.0 и в настоящее время запланирован на ноябрь 2021 года одновременно с .NET 6. EF Core 6.0 будет соответствовать .NET 6 в качестве выпуска с долгосрочной поддержкой (LTS). Он не будет работать в .NET Framework и маловероятно, что будет поддерживать какую-либо версию .NET Standard. Приведённые ниже планы – это пока лишь предложения от сообщества, и они не обязательно будут реализованы. Кроме того, может появиться и что-то другое.
Самые востребованные функции
1. Темпоральные таблицы SQL Server
Создание темпоральных таблиц с помощью миграции, а также доступ к историческим данным с помощью запросов LINQ.
2. Столбцы типа JSON
Общие шаблоны для поддержки JSON, которые могут быть реализованы любым поставщиком базы данных. Поддержка столбцов JSON будет реализована для SQL Server и SQLite (PostgreSQL и MySQL уже их поддерживают).
3. ColumnAttribute.Order
Произвольный порядок столбцов при создании таблицы с помощью Migrations или EnsureCreated.
Производительность
1. Инфраструктура производительности и новые тесты
Улучшение инфраструктуры для тестов производительности, а также добавление новых тестов и исправление ошибок.
2. Скомпилированные модели
Скомпилированные модели улучшат производительность запуска, а также в целом улучшат производительность при доступе к модели.
3. TechEmpower Fortunes
Планируется сравниться по производительности Dapper на тесте TechEmpower Fortunes.
4. Компоновщики/AOT
Продолжатся исследования по улучшению работы EF Core с компоновщиками и AOT.
Миграции и развертывание
1. Пакеты миграций
Пакеты миграции предоставят простой и надежный механизм для развертывания миграций EF Core.
2. Управление миграциями
Планируется улучшить инструменты и управление проектами/сборками для миграций EF Core.
Кроме того, планируется улучшение существующих функций и исправление известных ошибок. Полный план на EF Core 6.0 можно найти здесь. Также вы можете голосовать за новые функции или оставлять свои предложения в GitHub.
Источник: https://devblogs.microsoft.com/dotnet/the-plan-for-entity-framework-core-6-0/