День 1802. #TipsAndTricks
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5
6. Рефлексия и деревья выражений
Деревья выражений могут быть полезны для сложных сценариев рефлексии, таких как создание динамических методов или оптимизация путей кода, критичных к производительности.
Вывод:
Как это работает и почему это полезно:
В примере выше метод GeneratePropertyGetter<T> демонстрирует, как использовать деревья выражений для создания метода получения свойств для заданного класса и имени свойства. Метод принимает параметр типа T и строку, представляющую имя свойства, затем создаёт дерево выражений, которое предоставляет доступ к свойству в экземпляре T и возвращает его значение.
Дерево выражений создается с использованием методов класса Expression, таких как Expression.Parameter, Expression.Property и Expression.Lambda. После завершения создания дерева выражений вызывается метод Compile для создания делегата Func<T, object>, который можно использовать для вызова метода получения свойств во время выполнения. Метод GeneratePropertyGetter используется для создания методов получения свойств для свойства Name и Age записи Person. Эти методы получения свойств затем используются для получения значений свойств из экземпляра Person.
Использование деревьев выражений в связке с рефлексией может дать несколько преимуществ, таких как повышение производительности, гибкость и возможность генерировать и компилировать код во время выполнения. Однако имейте в виду, что деревья выражений могут быть более сложными и трудными для отладки, чем традиционные методы рефлексии, поэтому используйте их разумно и только при необходимости.
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5
6. Рефлексия и деревья выражений
Деревья выражений могут быть полезны для сложных сценариев рефлексии, таких как создание динамических методов или оптимизация путей кода, критичных к производительности.
Func<T, object> GeneratePropertyGetter<T>(
string property
)
{
var param =
Expression.Parameter(typeof(T), "obj");
var prop =
Expression.Property(param, property);
var conv =
Expression.Convert(prop, typeof(object));
var lambda =
Expression.Lambda<Func<T, object>>(conv, param);
return lambda.Compile();
}
// использование
var person = new Person("John Doe", 30);
var getName = GeneratePropertyGetter<Person>("Name");
var getAge = GeneratePropertyGetter<Person>("Age");
Console.WriteLine(
$"Name: {getName(person)}, Age: {getAge(person)}");
record Person(string Name, int Age);
Вывод:
Name: John Doe, Age: 30
Как это работает и почему это полезно:
В примере выше метод GeneratePropertyGetter<T> демонстрирует, как использовать деревья выражений для создания метода получения свойств для заданного класса и имени свойства. Метод принимает параметр типа T и строку, представляющую имя свойства, затем создаёт дерево выражений, которое предоставляет доступ к свойству в экземпляре T и возвращает его значение.
Дерево выражений создается с использованием методов класса Expression, таких как Expression.Parameter, Expression.Property и Expression.Lambda. После завершения создания дерева выражений вызывается метод Compile для создания делегата Func<T, object>, который можно использовать для вызова метода получения свойств во время выполнения. Метод GeneratePropertyGetter используется для создания методов получения свойств для свойства Name и Age записи Person. Эти методы получения свойств затем используются для получения значений свойств из экземпляра Person.
Использование деревьев выражений в связке с рефлексией может дать несколько преимуществ, таких как повышение производительности, гибкость и возможность генерировать и компилировать код во время выполнения. Однако имейте в виду, что деревья выражений могут быть более сложными и трудными для отладки, чем традиционные методы рефлексии, поэтому используйте их разумно и только при необходимости.
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
👍11
День 1803. #TipsAndTricks
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5
6
7. Упрощение многопоточности с помощью каналов
Каналы — это примитив синхронизации, представленный в .NET Core 3.0, который упрощает многопоточность, предоставляя потокам возможность взаимодействовать и обмениваться данными в потокобезопасном режиме. Их можно использовать для реализации шаблона производитель-потребитель, позволяющего разделить производство и потребление данных.
Как это работает и почему это полезно:
В примере выше метод ProcessData демонстрирует простой сценарий производитель-потребитель с использованием канала. Переменная канала инициализируется как неограниченный канал, то есть может хранить неограниченное количество элементов.
Задача производителя генерирует данные (целые числа от 1 до 10) и записывает их в канал с помощью метода WriteAsync. Задача потребителя считывает данные из канала с помощью метода ReadAllAsync и обрабатывает их. В этом случае она просто выводит на консоль полученные данные.
Задачи производителя и потребителя выполняются одновременно, что позволяет потребителю обрабатывать данные, как только они становятся доступными. Класс Channel гарантирует, что обмен данными является потокобезопасным, что упрощает написание многопоточного кода, не беспокоясь о блокировках или других механизмах синхронизации.
Каналы можно использовать в различных сценариях, таких как конвейеры обработки данных, распараллеливание рабочих нагрузок или реализация связи между компонентами в многопоточном приложении. Они предоставляют простой в использовании и эффективный способ управления параллелизмом и обменом данными между потоками.
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5
6
7. Упрощение многопоточности с помощью каналов
Каналы — это примитив синхронизации, представленный в .NET Core 3.0, который упрощает многопоточность, предоставляя потокам возможность взаимодействовать и обмениваться данными в потокобезопасном режиме. Их можно использовать для реализации шаблона производитель-потребитель, позволяющего разделить производство и потребление данных.
async Task ProcessData()
{
var channel = Channel.CreateUnbounded<int>();
var producer = Task.Run(async () =>
{
for (int i = 1; i <= 10; i++)
{
Console.WriteLine($"Произведено: {i}");
await channel.Writer.WriteAsync(i);
await Task.Delay(1000);
}
channel.Writer.Complete();
});
var consumer = Task.Run(async () =>
{
await foreach (
var i in channel.Reader.ReadAllAsync())
{
Console.WriteLine($"Обработано: {i}");
}
});
await Task.WhenAll(producer, consumer);
}
Как это работает и почему это полезно:
В примере выше метод ProcessData демонстрирует простой сценарий производитель-потребитель с использованием канала. Переменная канала инициализируется как неограниченный канал, то есть может хранить неограниченное количество элементов.
Задача производителя генерирует данные (целые числа от 1 до 10) и записывает их в канал с помощью метода WriteAsync. Задача потребителя считывает данные из канала с помощью метода ReadAllAsync и обрабатывает их. В этом случае она просто выводит на консоль полученные данные.
Задачи производителя и потребителя выполняются одновременно, что позволяет потребителю обрабатывать данные, как только они становятся доступными. Класс Channel гарантирует, что обмен данными является потокобезопасным, что упрощает написание многопоточного кода, не беспокоясь о блокировках или других механизмах синхронизации.
Каналы можно использовать в различных сценариях, таких как конвейеры обработки данных, распараллеливание рабочих нагрузок или реализация связи между компонентами в многопоточном приложении. Они предоставляют простой в использовании и эффективный способ управления параллелизмом и обменом данными между потоками.
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
👍31
День 1804. #TipsAndTricks
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5
6
7
8. Динамическая компиляция кода с Roslyn
Динамическая компиляция кода с помощью Roslyn позволяет компилировать и выполнять код C# во время выполнения. Это может быть полезно для сценариев, плагинов или ситуаций, когда код необходимо генерировать или изменять «на лету».
Вывод:
Как это работает и почему это полезно:
В примере выше метод ExecuteDynamicCodeAsync демонстрирует, как скомпилировать и выполнить фрагмент кода C# во время выполнения с помощью компилятора Roslyn. Метод Roslyn CSharpSyntaxTree.ParseText используется для анализа исходного кода в синтаксическое дерево, которое затем добавляется в новый объект CSharpCompilation. К объекту компиляции также добавляются необходимые ссылки на сборки: библиотеку CoreLib, System.Runtime и System.Console для работы класса Console. Заметьте, что в последних версиях .NET они находятся в разных файлах.
Метод Emit компилирует код в динамически подключаемую библиотеку (DLL) и записывает выходные данные в MemoryStream. Если компиляция прошла успешно, полученная сборка загружается в текущий домен приложения с помощью метода Assembly.Load. Затем класс Runner и его метод Run получаются через рефлексию, и этот метод вызывается, выполняя динамический код.
Этот метод позволяет создавать гибкие и расширяемые приложения, которые могут динамически компилировать и выполнять код C# во время выполнения. Однако будьте осторожны с последствиями для безопасности, поскольку выполнение произвольного кода может создать угрозу безопасности, если с ним не обращаться должным образом.
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5
6
7
8. Динамическая компиляция кода с Roslyn
Динамическая компиляция кода с помощью Roslyn позволяет компилировать и выполнять код C# во время выполнения. Это может быть полезно для сценариев, плагинов или ситуаций, когда код необходимо генерировать или изменять «на лету».
using System.Reflection;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
async Task ExecuteDynamicCodeAsync(
string code)
{
string sourceCode = $@"
using System;
namespace DynamicCode;
public class Runner
{{
public static void Run()
{{
{code}
}}
}}
";
var syntaxTree = CSharpSyntaxTree.ParseText(sourceCode);
var assemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location);
var references = new List<MetadataReference>
{
MetadataReference.CreateFromFile(
Path.Combine(assemblyPath, "System.Private.CoreLib.dll")),
MetadataReference.CreateFromFile(
Path.Combine(assemblyPath, "System.Console.dll")),
MetadataReference.CreateFromFile(
Path.Combine(assemblyPath, "System.Runtime.dll"))
};
var compilation = CSharpCompilation.Create("DynamicCode")
.WithOptions(new CSharpCompilationOptions(
OutputKind.DynamicallyLinkedLibrary))
.AddReferences(references)
.AddSyntaxTrees(syntaxTree);
using var ms = new MemoryStream();
var result = compilation.Emit(ms);
if (!result.Success)
{
Console.WriteLine("Компиляция не удалась");
return;
}
ms.Seek(0, SeekOrigin.Begin);
var assembly = Assembly.Load(ms.ToArray());
var type = assembly.GetType("DynamicCode.Runner");
var method = type.GetMethod("Run",
BindingFlags.Static | BindingFlags.Public);
method.Invoke(null, null);
}
// использование
await ExecuteDynamicCodeAsync(
"Console.WriteLine(\"Привет, динамический код!\");"
);
Вывод:
Привет, динамический код!
Как это работает и почему это полезно:
В примере выше метод ExecuteDynamicCodeAsync демонстрирует, как скомпилировать и выполнить фрагмент кода C# во время выполнения с помощью компилятора Roslyn. Метод Roslyn CSharpSyntaxTree.ParseText используется для анализа исходного кода в синтаксическое дерево, которое затем добавляется в новый объект CSharpCompilation. К объекту компиляции также добавляются необходимые ссылки на сборки: библиотеку CoreLib, System.Runtime и System.Console для работы класса Console. Заметьте, что в последних версиях .NET они находятся в разных файлах.
Метод Emit компилирует код в динамически подключаемую библиотеку (DLL) и записывает выходные данные в MemoryStream. Если компиляция прошла успешно, полученная сборка загружается в текущий домен приложения с помощью метода Assembly.Load. Затем класс Runner и его метод Run получаются через рефлексию, и этот метод вызывается, выполняя динамический код.
Этот метод позволяет создавать гибкие и расширяемые приложения, которые могут динамически компилировать и выполнять код C# во время выполнения. Однако будьте осторожны с последствиями для безопасности, поскольку выполнение произвольного кода может создать угрозу безопасности, если с ним не обращаться должным образом.
Продолжение следует…
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
👍15
День 1805.
Итак, результаты розыгрыша подарочных кружек. Как обычно, определил его величество рандом. Кружек две и победителей двое.
@juju_true и @NataliEgorsheva, поздравляю! 🥳
Пожалуйста, свяжитесь со мной в личке.
Итак, результаты розыгрыша подарочных кружек. Как обычно, определил его величество рандом. Кружек две и победителей двое.
@juju_true и @NataliEgorsheva, поздравляю! 🥳
Пожалуйста, свяжитесь со мной в личке.
👍18👎1
День 1806. #TipsAndTricks
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5
6
7
8
9. Преобразование анонимных типов в dynamic
Преобразование анонимных типов в объекты dynamic может обеспечить большую гибкость при манипулировании данными. Анонимные типы доступны только для чтения и строго типизированы, что может ограничивать возможности изменения или расширения данных. Преобразуя анонимный тип в динамический
ExpandoObject вы получаете возможность добавлять, удалять или изменять свойства во время выполнения.
Как это работает и почему это полезно:
В примере выше метод ToDynamic принимает анонимный объект в качестве входных данных и преобразует его в динамический ExpandoObject. Это делается путем перебора свойств анонимного объекта с помощью TypeDescriptor.GetProperties и добавления их в ExpandoObject с помощью интерфейса IDictionary<string, object>.
Далее демонстрируется, как использовать метод ToDynamic для преобразования анонимного объекта в динамический объект. Переменная anon содержит анонимный объект со свойствами Name и Age. После преобразования его в динамический объект с помощью ToDynamic вы можете напрямую получать доступ к его свойствам и изменять их, а также добавлять новые свойства, такие как City.
Преобразование анонимных типов в динамические объекты может быть полезно, когда вам нужна большая гибкость со структурами данных, особенно при работе с динамически генерируемыми данными или когда схема неизвестна во время компиляции. Однако имейте в виду, что использование динамических объектов может привести к потере проверки типов во время компиляции и потенциальным проблемам производительности.
Окончание следует...
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
10 Крутых Трюков в C#. Продолжение
1-2
3-4
5
6
7
8
9. Преобразование анонимных типов в dynamic
Преобразование анонимных типов в объекты dynamic может обеспечить большую гибкость при манипулировании данными. Анонимные типы доступны только для чтения и строго типизированы, что может ограничивать возможности изменения или расширения данных. Преобразуя анонимный тип в динамический
ExpandoObject вы получаете возможность добавлять, удалять или изменять свойства во время выполнения.
dynamic ToDynamic(object anon)
{
var dyn = new ExpandoObject()
as IDictionary<string, object>;
var props = TypeDescriptor.GetProperties(anon);
foreach (PropertyDescriptor p in props)
dyn.Add(p.Name, p.GetValue(anon));
return dyn;
}
// использование
var anon = new { Name = "John", Age = 30 };
dynamic dyn = ToDynamic(anon);
Console.WriteLine(
$"Name: {dyn.Name}, Age: {dyn.Age}");
dyn.Age = 35;
dyn.City = "New York";
Console.WriteLine(
@$"Name: {dyn.Name},
Age: {dyn.Age},
City:{dyn.City}");
Как это работает и почему это полезно:
В примере выше метод ToDynamic принимает анонимный объект в качестве входных данных и преобразует его в динамический ExpandoObject. Это делается путем перебора свойств анонимного объекта с помощью TypeDescriptor.GetProperties и добавления их в ExpandoObject с помощью интерфейса IDictionary<string, object>.
Далее демонстрируется, как использовать метод ToDynamic для преобразования анонимного объекта в динамический объект. Переменная anon содержит анонимный объект со свойствами Name и Age. После преобразования его в динамический объект с помощью ToDynamic вы можете напрямую получать доступ к его свойствам и изменять их, а также добавлять новые свойства, такие как City.
Преобразование анонимных типов в динамические объекты может быть полезно, когда вам нужна большая гибкость со структурами данных, особенно при работе с динамически генерируемыми данными или когда схема неизвестна во время компиляции. Однако имейте в виду, что использование динамических объектов может привести к потере проверки типов во время компиляции и потенциальным проблемам производительности.
Окончание следует...
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
👍7👎1
День 1807. #TipsAndTricks
10 Крутых Трюков в C#. Окончание
1-2
3-4
5
6
7
8
9
10. Простой пул объектов для повторно используемых ресурсов
Пул объектов — это шаблон проектирования, который помогает повторно использовать объекты, создание которых требует больших затрат, например соединения с базой данных или большие буферы памяти. Создав пул предварительно выделенных объектов и повторно используя их при необходимости, вы можете повысить производительность приложения и снизить накладные расходы, связанные с созданием и уничтожением объектов.
Как это работает и почему это полезно:
В примере выше класс ObjectPool<T> является обобщённой реализацией пула объектов. Он использует ConcurrentBag<T> для хранения объектов и делегат Func<T> для создания новых объектов при необходимости. Метод Get извлекает объект из пула, если он доступен, или создает новый, если пул пуст. Метод Return возвращает объект в пул, когда он больше не нужен. Класс Expensive представляет гипотетический ресурс, создание которого требует больших затрат.
В примере использования создается экземпляр ObjectPool<Expensive>, а экземпляр Expensive извлекается из пула с помощью метода Get. После манипуляции со свойствами объекта вызывается метод Return, который возвращает объект в пул для будущего повторного использования.
Использование пула объектов может повысить производительность приложения и сократить использование памяти за счет минимизации количества созданий и удалений объектов. Это особенно полезно в высокопроизводительных средах или средах с ограниченными ресурсами, где создание объектов и сборка мусора могут стать серьёзными узкими местами. Однако имейте в виду, что объединение объектов в пул усложняет код и может подходить не для всех сценариев.
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
10 Крутых Трюков в C#. Окончание
1-2
3-4
5
6
7
8
9
10. Простой пул объектов для повторно используемых ресурсов
Пул объектов — это шаблон проектирования, который помогает повторно использовать объекты, создание которых требует больших затрат, например соединения с базой данных или большие буферы памяти. Создав пул предварительно выделенных объектов и повторно используя их при необходимости, вы можете повысить производительность приложения и снизить накладные расходы, связанные с созданием и уничтожением объектов.
class ObjectPool<T> where T : new()
{
private readonly ConcurrentBag<T> _objects;
private readonly Func<T> _generator;
public ObjectPool(Func<T>? generator = null)
{
_generator = generator ??
(() => new T());
_objects = [];
}
public T Get()
{
return _objects.TryTake(out T item)
? item
: _generator();
}
public void Return(T item)
{
_objects.Add(item);
}
}
class Expensive
{
public int Value { get; set; }
}
// использование
var pool = new ObjectPool<Expensive>();
var resource = pool.Get();
resource.Value = 42;
Console.WriteLine(
$"Значение ресурса: {resource.Value}");
pool.Return(resource);
Как это работает и почему это полезно:
В примере выше класс ObjectPool<T> является обобщённой реализацией пула объектов. Он использует ConcurrentBag<T> для хранения объектов и делегат Func<T> для создания новых объектов при необходимости. Метод Get извлекает объект из пула, если он доступен, или создает новый, если пул пуст. Метод Return возвращает объект в пул, когда он больше не нужен. Класс Expensive представляет гипотетический ресурс, создание которого требует больших затрат.
В примере использования создается экземпляр ObjectPool<Expensive>, а экземпляр Expensive извлекается из пула с помощью метода Get. После манипуляции со свойствами объекта вызывается метод Return, который возвращает объект в пул для будущего повторного использования.
Использование пула объектов может повысить производительность приложения и сократить использование памяти за счет минимизации количества созданий и удалений объектов. Это особенно полезно в высокопроизводительных средах или средах с ограниченными ресурсами, где создание объектов и сборка мусора могут стать серьёзными узкими местами. Однако имейте в виду, что объединение объектов в пул усложняет код и может подходить не для всех сценариев.
Источник: https://maherz.medium.com/10-mind-blowing-c-hacks-95fa629cfcef
👍9👎1
День 1808. #ЧтоНовенького #CSharp13
Коллекции в Params
Всего месяц после выпуска .NET 8, а команда dotnet уже работает над следующей итерацией: .NET 9. Сегодня посмотрим на одно из предлагаемых нововведений: коллекции в параметрах с ключевым словом params.
Ключевое слово params в C# появилось давно. Оно позволяет передавать в метод переменное количество аргументов:
До сих пор оно было доступно только для массивов. Нововведение позволяет использовать его для коллекций любого типа:
И теперь эти методы могут быть вызваны любым способом:
Это позволяет добавить вариативности в использовании ваших методов клиентами и иметь более эффективный код внутри метода. Для полноты картины, в C# 12 вы уже могли сделать так:
Тем не менее, новая версия удобнее. Также обещают решить проблемы производительности, присущие нынешней версии params, но это не точно (с).
Подробнее про нововведение можно почитать в предложении на GitHub или попробовать его в действии на SharpLab.
Источник: https://steven-giesel.com/blogPost/5f4fef86-d251-4a47-b893-ca4c515ca314/the-first-possible-new-feature-of-c-13-params-collection
Коллекции в Params
Всего месяц после выпуска .NET 8, а команда dotnet уже работает над следующей итерацией: .NET 9. Сегодня посмотрим на одно из предлагаемых нововведений: коллекции в параметрах с ключевым словом params.
Ключевое слово params в C# появилось давно. Оно позволяет передавать в метод переменное количество аргументов:
void PrintNums(params int[] nums)
{
foreach (var n in nums)
Console.WriteLine(n);
}
До сих пор оно было доступно только для массивов. Нововведение позволяет использовать его для коллекций любого типа:
void PrintNumsSpan(
params ReadOnlySpan<int> nums)
{
foreach (var n in nums)
Console.WriteLine(n);
}
void PrintNumsList(
params List<int> nums)
{
nums.ForEach(Console.WriteLine);
}
И теперь эти методы могут быть вызваны любым способом:
PrintNumsSpan(1, 2, 3);
PrintNumsList([1, 2, 3]);
Это позволяет добавить вариативности в использовании ваших методов клиентами и иметь более эффективный код внутри метода. Для полноты картины, в C# 12 вы уже могли сделать так:
List<int> numbers = [1,2,3];
PrintNums([..numbers]);
Тем не менее, новая версия удобнее. Также обещают решить проблемы производительности, присущие нынешней версии params, но это не точно (с).
Подробнее про нововведение можно почитать в предложении на GitHub или попробовать его в действии на SharpLab.
Источник: https://steven-giesel.com/blogPost/5f4fef86-d251-4a47-b893-ca4c515ca314/the-first-possible-new-feature-of-c-13-params-collection
👍22
День 1809. #Оффтоп
Самая Сложная Часть Создания ПО — не Кодирование, а ТЗ. Начало
Глядя на новости о разработках в области искусственного интеллекта, многие боятся того, что мы, как разработчики ПО, вскоре можем остаться без работы, и нас заменит ИИ. Они полагают, что бизнес будет напрямую просить ИИ создать то, что, по их мнению, им нужно. Стоит ли этого бояться?
Программирование может оказаться непростой задачей, но разобраться в коде, в принципе, не так сложно. Как только вы освоите синтаксис, логику и методы, написание кода станет довольно простым процессом в большинстве случаев. Настоящие проблемы обычно связаны с тем, что должно делать ПО. Самое сложное в создании ПО — создание требований, а требования к ПО по-прежнему определяются людьми.
Это не баг, это фича… нет, погодите, это баг
Я как-то работал над созданием ПО, которое настраивало индивидуальные условия для продуктов на сайтах электронной торговли. Существовало размытое описание логики, которая зависела от типа товара и штата США, где находился заказчик в силу требований закона.
В какой-то момент мне показалось, что я нашел баг. Пользователь выбирает один тип продукта, что создаёт соответствующие условия, но далее в рабочем процессе он может изменить тип продукта. Это нарушало условия, под которыми стояла подпись клиента. Я спросил клиента, надо ли убрать возможность менять тип продукта или при смене переопределять условия? Ответ был уверенным: "Этого никогда не произойдёт".
Это был старший руководитель, не было повода ему не доверять. Несколько месяцев спустя, перед релизом ПО, тестировщик обнаружил дефект, и его поручил мне. Угадайте, что это было, кого в этом обвинили и кого попросили это исправить? Исправить ошибку было легко, а последствия были незначительными, но это лишь в этот раз. Проблемы больше, их сложнее исправить, и они обходятся дороже, чем дальше в процессе разработки вы продвигаетесь. Но источник проблем обычно один и тот же: требования были неясными, непоследовательными или неправильными.
ИИ сейчас: шахматы против беспилотных автомобилей
Концепция ИИ существует уже довольно давно. Он был применён в шахматах ещё в 80-х годах. К концу 90-х ИИ превзошёл возможности человека выигрывать в шахматы. Это тоже неудивительно, ведь параметры шахмат КОНЕЧНЫ. При каждом ходе существует конечное число возможных ходов. ИИ может рассчитывать последствия каждого хода, чтобы выбрать наиболее подходящий, приводящий в итоге к победе.
Другая сфера - беспилотные автомобили. Производители уже довольно давно их обещают. Они в основном используют механизмы, основанные на правилах, для принятия решений. Но здесь, в отличие от шахмат, правила поведения во всех возможных ситуациях чётко не определены. Водители в поездке принимают тысячи мелких решений. Правильность этих решений часто означает разницу между прибытием в пункт назначения и аварией.
В сфере технологий есть понятие для выражения доступности сервиса в виде количества девяток. Стандартом сейчас является 5 девяток, т.е. доступность 99,999% времени. Проблема в том, что достичь уровня доступности в 99% легко, но каждая следующая девятка даётся сложнее в геометрической прогрессии.
Причина, по которой так сложно достичь приемлемого уровня безопасности беспилотных авто в том, что вождение автомобиля предполагает значительно больше переменных, чем шахматы, и эти переменные НЕ КОНЕЧНЫ. Первые 95% или 99% могут быть предсказуемыми и легко поддающимися учёту. Но дальше существует очень много крайних случаев, которые могут иметь некоторые общие черты, но каждый из которых уникален: другие машины, управляемые людьми, перекрытия дорог, аварии, погодные явления. Невероятно сложно заставить модель ИИ учитывать и распознавать эти аномалии, и, что более важно, правильно реагировать, не попадая в аварию.
Окончание следует…
Источник: https://stackoverflow.blog/2023/12/29/the-hardest-part-of-building-software-is-not-coding-its-requirements/
Автор оригинала: Jared Toporek (консультант в разработке ПО, создатель приложения Keenforms)
Самая Сложная Часть Создания ПО — не Кодирование, а ТЗ. Начало
Глядя на новости о разработках в области искусственного интеллекта, многие боятся того, что мы, как разработчики ПО, вскоре можем остаться без работы, и нас заменит ИИ. Они полагают, что бизнес будет напрямую просить ИИ создать то, что, по их мнению, им нужно. Стоит ли этого бояться?
Программирование может оказаться непростой задачей, но разобраться в коде, в принципе, не так сложно. Как только вы освоите синтаксис, логику и методы, написание кода станет довольно простым процессом в большинстве случаев. Настоящие проблемы обычно связаны с тем, что должно делать ПО. Самое сложное в создании ПО — создание требований, а требования к ПО по-прежнему определяются людьми.
Это не баг, это фича… нет, погодите, это баг
Я как-то работал над созданием ПО, которое настраивало индивидуальные условия для продуктов на сайтах электронной торговли. Существовало размытое описание логики, которая зависела от типа товара и штата США, где находился заказчик в силу требований закона.
В какой-то момент мне показалось, что я нашел баг. Пользователь выбирает один тип продукта, что создаёт соответствующие условия, но далее в рабочем процессе он может изменить тип продукта. Это нарушало условия, под которыми стояла подпись клиента. Я спросил клиента, надо ли убрать возможность менять тип продукта или при смене переопределять условия? Ответ был уверенным: "Этого никогда не произойдёт".
Это был старший руководитель, не было повода ему не доверять. Несколько месяцев спустя, перед релизом ПО, тестировщик обнаружил дефект, и его поручил мне. Угадайте, что это было, кого в этом обвинили и кого попросили это исправить? Исправить ошибку было легко, а последствия были незначительными, но это лишь в этот раз. Проблемы больше, их сложнее исправить, и они обходятся дороже, чем дальше в процессе разработки вы продвигаетесь. Но источник проблем обычно один и тот же: требования были неясными, непоследовательными или неправильными.
ИИ сейчас: шахматы против беспилотных автомобилей
Концепция ИИ существует уже довольно давно. Он был применён в шахматах ещё в 80-х годах. К концу 90-х ИИ превзошёл возможности человека выигрывать в шахматы. Это тоже неудивительно, ведь параметры шахмат КОНЕЧНЫ. При каждом ходе существует конечное число возможных ходов. ИИ может рассчитывать последствия каждого хода, чтобы выбрать наиболее подходящий, приводящий в итоге к победе.
Другая сфера - беспилотные автомобили. Производители уже довольно давно их обещают. Они в основном используют механизмы, основанные на правилах, для принятия решений. Но здесь, в отличие от шахмат, правила поведения во всех возможных ситуациях чётко не определены. Водители в поездке принимают тысячи мелких решений. Правильность этих решений часто означает разницу между прибытием в пункт назначения и аварией.
В сфере технологий есть понятие для выражения доступности сервиса в виде количества девяток. Стандартом сейчас является 5 девяток, т.е. доступность 99,999% времени. Проблема в том, что достичь уровня доступности в 99% легко, но каждая следующая девятка даётся сложнее в геометрической прогрессии.
Причина, по которой так сложно достичь приемлемого уровня безопасности беспилотных авто в том, что вождение автомобиля предполагает значительно больше переменных, чем шахматы, и эти переменные НЕ КОНЕЧНЫ. Первые 95% или 99% могут быть предсказуемыми и легко поддающимися учёту. Но дальше существует очень много крайних случаев, которые могут иметь некоторые общие черты, но каждый из которых уникален: другие машины, управляемые людьми, перекрытия дорог, аварии, погодные явления. Невероятно сложно заставить модель ИИ учитывать и распознавать эти аномалии, и, что более важно, правильно реагировать, не попадая в аварию.
Окончание следует…
Источник: https://stackoverflow.blog/2023/12/29/the-hardest-part-of-building-software-is-not-coding-its-requirements/
Автор оригинала: Jared Toporek (консультант в разработке ПО, создатель приложения Keenforms)
👍20
День 1810. #Оффтоп
Самая Сложная Часть Создания ПО — не Кодирование, а ТЗ. Окончание
Начало
ИИ не может создавать ПО, только код
Создание и поддержка ПО имеет гораздо больше общего с вождением автомобиля, чем с игрой в шахматы. Здесь больше переменных, и правила основаны на суждениях. Когда вы создаёте ПО, у вас может быть желаемый результат, но маловероятно, что он будет таким же строго рассчитанным, как в шахматах. ПО редко создаётся раз и навсегда. Добавляются функции и исправляются ошибки — это постоянная эволюция.
В разработке есть инструмент, позволяющий приблизить наши проекты к строго контролируемому механизму правил шахмат: технические задания. В лучшем случае ТЗ описывает ожидаемое поведение пользователей и ход выполнения программы. Чтобы купить товар, пользователь нажимает эту кнопку, создаёт эту структуру данных, запускает этот сервис. Однако чаще нам вручают списки пожеланий функций, макеты на салфетке и нечёткие документы, и говорят, чтобы мы из этого сделали что-то рабочее. Хуже того, требования меняются или игнорируются.
Недавно меня попросили помочь команде создать опросник для помощи людям по вопросам здоровья. Приложение должно было работать в регионах без надёжного интернета, поэтому решено было проводить опросы через SMS. Как только я начал слушать описание требований, я понял, что это будет проблемой. Одно дело, когда компания спрашивает вас по шкале от 1 до 10, насколько вероятно, что вы снова воспользуетесь их сервисом. Совсем другое дело — создать многоэтапный опрос с вопросами с несколькими вариантами ответов о симптомах. Я не отказался, но упомянул все возможные точки сбоя в этом процессе и хотел, чтобы команда чётко определила, как мы будем обрабатывать поступающие ответы на все вопросы. Номера вариантов ответа через запятую? Что если число в ответе не соответствует ни одному из вариантов?
После всех этих вопросов команда пришла к выводу: лучше не делать это приложение. Как ни странно, я бы сказал, что это успешный результат работы. Было бы более расточительно продолжать работу без чёткого разрешения всех потенциальных ошибок. Сможет ли ИИ так же разубедить клиентов создавать приложение? И будет ли вообще задавать наводящие вопросы, предупреждая обо всех возможных проблемах?
Чтобы создать функционирующее ПО с помощью ИИ, необходимо знать, чего вы хотите, и уметь ясно и точно это определить. Бывают случаи, когда я пишу ПО только для себя и не осознаю некоторых трудностей и проблем, пока фактически не начну писать код.
За последнее десятилетие индустрия программного обеспечения перешла от каскадной методологии (Waterfall) к гибкой (Agile). Waterfall точно определяет, что вы хотите, ещё до написания кода, а Agile обеспечивает достаточную гибкость, чтобы вы могли вносить изменения по ходу.
Много проектов, использующих Waterfall, потерпели неудачу, потому что люди думали, что знают, чего хотят, и могут точно описать это, но были разочарованы в конечном продукте.
Возможно, ИИ лучше подойдёт для переписывания уже имеющегося ПО, под новое оборудование или новые версии языков и фреймворков. По-прежнему существует гигантское количество ПО, написанного на устаревшем языке и всё меньше людей этот язык изучающих. Если вы точно знаете, чего хотите, возможно, вы сможете заставить ИИ производить ПО быстрее и дешевле, чем команда программистов-людей. Я верю, что ИИ сможет сделать уже созданное ПО быстрее, чем программисты-люди, но это потому, что кто-то по ходу разработки понял, что это ПО должно делать.
ИИ может неплохо справляться с созданием ПО по модели Waterfall. Проблема в этой модели - в людях. Не там, где подписанные ТЗ передаются команде программистов для написания кода, а во всём том, что было до этого. ИИ может делать некоторые необычные вещи, но он не может читать ваши мысли или подсказывать, чего вам следует хотеть.
Источник: https://stackoverflow.blog/2023/12/29/the-hardest-part-of-building-software-is-not-coding-its-requirements/
Автор оригинала: Jared Toporek (консультант в разработке ПО, создатель приложения Keenforms).
Самая Сложная Часть Создания ПО — не Кодирование, а ТЗ. Окончание
Начало
ИИ не может создавать ПО, только код
Создание и поддержка ПО имеет гораздо больше общего с вождением автомобиля, чем с игрой в шахматы. Здесь больше переменных, и правила основаны на суждениях. Когда вы создаёте ПО, у вас может быть желаемый результат, но маловероятно, что он будет таким же строго рассчитанным, как в шахматах. ПО редко создаётся раз и навсегда. Добавляются функции и исправляются ошибки — это постоянная эволюция.
В разработке есть инструмент, позволяющий приблизить наши проекты к строго контролируемому механизму правил шахмат: технические задания. В лучшем случае ТЗ описывает ожидаемое поведение пользователей и ход выполнения программы. Чтобы купить товар, пользователь нажимает эту кнопку, создаёт эту структуру данных, запускает этот сервис. Однако чаще нам вручают списки пожеланий функций, макеты на салфетке и нечёткие документы, и говорят, чтобы мы из этого сделали что-то рабочее. Хуже того, требования меняются или игнорируются.
Недавно меня попросили помочь команде создать опросник для помощи людям по вопросам здоровья. Приложение должно было работать в регионах без надёжного интернета, поэтому решено было проводить опросы через SMS. Как только я начал слушать описание требований, я понял, что это будет проблемой. Одно дело, когда компания спрашивает вас по шкале от 1 до 10, насколько вероятно, что вы снова воспользуетесь их сервисом. Совсем другое дело — создать многоэтапный опрос с вопросами с несколькими вариантами ответов о симптомах. Я не отказался, но упомянул все возможные точки сбоя в этом процессе и хотел, чтобы команда чётко определила, как мы будем обрабатывать поступающие ответы на все вопросы. Номера вариантов ответа через запятую? Что если число в ответе не соответствует ни одному из вариантов?
После всех этих вопросов команда пришла к выводу: лучше не делать это приложение. Как ни странно, я бы сказал, что это успешный результат работы. Было бы более расточительно продолжать работу без чёткого разрешения всех потенциальных ошибок. Сможет ли ИИ так же разубедить клиентов создавать приложение? И будет ли вообще задавать наводящие вопросы, предупреждая обо всех возможных проблемах?
Чтобы создать функционирующее ПО с помощью ИИ, необходимо знать, чего вы хотите, и уметь ясно и точно это определить. Бывают случаи, когда я пишу ПО только для себя и не осознаю некоторых трудностей и проблем, пока фактически не начну писать код.
За последнее десятилетие индустрия программного обеспечения перешла от каскадной методологии (Waterfall) к гибкой (Agile). Waterfall точно определяет, что вы хотите, ещё до написания кода, а Agile обеспечивает достаточную гибкость, чтобы вы могли вносить изменения по ходу.
Много проектов, использующих Waterfall, потерпели неудачу, потому что люди думали, что знают, чего хотят, и могут точно описать это, но были разочарованы в конечном продукте.
Возможно, ИИ лучше подойдёт для переписывания уже имеющегося ПО, под новое оборудование или новые версии языков и фреймворков. По-прежнему существует гигантское количество ПО, написанного на устаревшем языке и всё меньше людей этот язык изучающих. Если вы точно знаете, чего хотите, возможно, вы сможете заставить ИИ производить ПО быстрее и дешевле, чем команда программистов-людей. Я верю, что ИИ сможет сделать уже созданное ПО быстрее, чем программисты-люди, но это потому, что кто-то по ходу разработки понял, что это ПО должно делать.
ИИ может неплохо справляться с созданием ПО по модели Waterfall. Проблема в этой модели - в людях. Не там, где подписанные ТЗ передаются команде программистов для написания кода, а во всём том, что было до этого. ИИ может делать некоторые необычные вещи, но он не может читать ваши мысли или подсказывать, чего вам следует хотеть.
Источник: https://stackoverflow.blog/2023/12/29/the-hardest-part-of-building-software-is-not-coding-its-requirements/
Автор оригинала: Jared Toporek (консультант в разработке ПО, создатель приложения Keenforms).
👍19
День 1811. #ЗаметкиНаПолях
Слабые События в C#
События являются распространённым источником утечек памяти, когда подписчик забывает отписаться от события, а жизненный цикл подписчика и издателя события разный. Можно попробовать использовать слабые события. Это события, которые не мешают сборщику мусора удалять подписчика.
Слабые события работают на основе WeakReference<T> — ссылки на объект, которая не предотвращает сбор мусора для объекта. В примере ниже мы храним как слабые ссылки на события. Также мы сохраняем делегаты обработчиков в привязке к объекту обработчика в ConditionalWeakTable. Это предотвращает преждевременное удаление делегатов сборщиком мусора.
Вот пример, как использовать класс WeakEvent<TEventArgs>:
Источник: https://www.meziantou.net/weak-events-in-csharp.htm
Слабые События в C#
События являются распространённым источником утечек памяти, когда подписчик забывает отписаться от события, а жизненный цикл подписчика и издателя события разный. Можно попробовать использовать слабые события. Это события, которые не мешают сборщику мусора удалять подписчика.
Слабые события работают на основе WeakReference<T> — ссылки на объект, которая не предотвращает сбор мусора для объекта. В примере ниже мы храним как слабые ссылки на события. Также мы сохраняем делегаты обработчиков в привязке к объекту обработчика в ConditionalWeakTable. Это предотвращает преждевременное удаление делегатов сборщиком мусора.
class WeakEvent<TEventArgs>
{
private ImmutableList<
WeakReference<EventHandler<TEventArgs>>>
_listeners =
ImmutableList<
WeakReference<
EventHandler<TEventArgs>>>.Empty;
private readonly
ConditionalWeakTable<object, List<object>>
_delegates = [];
public void AddListener(
EventHandler<TEventArgs> handler)
{
if (handler == null)
return;
var weakRef = new
WeakReference<EventHandler<TEventArgs>>(handler);
_listeners = _listeners.Add(weakRef);
if (handler.Target != null)
_delegates
.GetOrCreateValue(handler.Target)
.Add(handler);
}
public void RemoveListener(
EventHandler<TEventArgs> handler)
{
if (handler == null)
return;
_listeners = _listeners
.RemoveAll(wr =>
!wr.TryGetTarget(out var target)
|| handler.Equals(target));
if (handler.Target != null
&& _delegates.TryGetValue(
handler.Target, out var weakRef))
{
weakRef.Remove(handler);
}
}
public void Raise(object? sender, TEventArgs args)
{
foreach (var l in _listeners)
{
if (l.TryGetTarget(out var target))
target.Invoke(sender, args);
else
{
// Удаляем делегат, если обработчик был удалён
_listeners = _listeners.Remove(l);
}
}
}
}
Вот пример, как использовать класс WeakEvent<TEventArgs>:
var sample = new Sample();
sample.CustomEvent +=
(sender, e) => Console.WriteLine("handler");
sample.DoAction();
class Sample
{
private readonly WeakEvent<EventArgs>
_event = new();
public event EventHandler<EventArgs> CustomEvent
{
add =>
_event.AddListener(value);
remove =>
_event.RemoveListener(value);
}
public void DoAction()
{
_event.Raise(this, EventArgs.Empty);
}
}
Источник: https://www.meziantou.net/weak-events-in-csharp.htm
👍9
День 1812. #TipsAndTricks
Используем Оператор Неявного Преобразования для Улучшения Читаемости
Оператор неявного преобразования — одна из самых крутых скрытых функций C#. И причина, по которой он не так популярен, заключается в том, что трудно придумать, как его использовать. Многие называют его неявным конструктором, понятно, откуда это взялось. Но давайте попробовать использовать его так, как Microsoft его задумали.
Оператор неявного преобразования позволяет нам без лишних усилий преобразовывать один тип в другой. Например:
У нас есть класс Color, который просто принимает в конструкторе шестнадцатеричный код цвета. Также есть класс Wall с методом Paint, который получает цвет.
Использование:
Мы создаём экземпляр класса Wall и вызываем метод Paint. Однако мы передаём строку, а не экземпляр Color. Как это возможно? Всё благодаря магии неявного оператора приведения. Если вы посмотрите на определение оператора в классе Color, вы увидите, что он вызывает конструктор, используя предоставленный шестнадцатеричный код.
Это очень простой сценарий, но его потенциал огромен. Например, вы можете использовать его для номеров паспортов, координат, URI, IP-адресов и многих других объектов-значений, элегантно решая проблему одержимости примитавами. Только не переусердствуйте и не используйте сверхсложные операторы.
Источник: https://intodot.net/using-implicit-conversion-operators-in-c-to-improve-readability/
Используем Оператор Неявного Преобразования для Улучшения Читаемости
Оператор неявного преобразования — одна из самых крутых скрытых функций C#. И причина, по которой он не так популярен, заключается в том, что трудно придумать, как его использовать. Многие называют его неявным конструктором, понятно, откуда это взялось. Но давайте попробовать использовать его так, как Microsoft его задумали.
Оператор неявного преобразования позволяет нам без лишних усилий преобразовывать один тип в другой. Например:
class Color
{
public string HexCode { get; set; }
public Color(string hexCode)
{
HexCode = hexCode;
}
public static implicit operator
Color(string hexCode) => new(hexCode);
}
class Wall
{
public void Paint(Color color) =>
Console.WriteLine($"Цвет: {color.HexCode}");
}
У нас есть класс Color, который просто принимает в конструкторе шестнадцатеричный код цвета. Также есть класс Wall с методом Paint, который получает цвет.
Использование:
var wall = new Wall();
wall.Paint("#FF0000");
Мы создаём экземпляр класса Wall и вызываем метод Paint. Однако мы передаём строку, а не экземпляр Color. Как это возможно? Всё благодаря магии неявного оператора приведения. Если вы посмотрите на определение оператора в классе Color, вы увидите, что он вызывает конструктор, используя предоставленный шестнадцатеричный код.
Это очень простой сценарий, но его потенциал огромен. Например, вы можете использовать его для номеров паспортов, координат, URI, IP-адресов и многих других объектов-значений, элегантно решая проблему одержимости примитавами. Только не переусердствуйте и не используйте сверхсложные операторы.
Источник: https://intodot.net/using-implicit-conversion-operators-in-c-to-improve-readability/
👍20
День 1813. #Testing
Практикуем TDD
Люди не пришли к единому мнению относительно определения процесса разработки через тестирование (TDD). Сегодня рассмотрим процесс Cannon TDD, предложенный Кентом Бэком. Если вы делаете что-то по-другому и это вам подходит, отлично!
TDD — это рабочий процесс написания ПО. Программисту необходимо изменить поведение системы (или создать её). TDD призван помочь создать новое состояние системы, в котором:
- Всё, что раньше работало, продолжает работать.
- Новое поведение работает так, как ожидалось.
- Система готова к следующим изменениям.
- Программист и его коллеги уверены в вышеперечисленных пунктах.
Разделение интерфейса/реализации
Первое недопонимание заключается в том, что люди смешивают всю разработку в одну кучу. Есть два сценария:
- Как вызывается конкретная часть поведения.
- Как система реализует такое поведение.
Шаги
Люди — паршивые компьютеры. Следующий процесс похож на алгоритм, но это не так. Он написан так в попытке эффективно общаться с людьми, привыкшими работать с программами. «Попытке», потому что люди склонны говорить: «TDD - отстой! Я сделал <совершенно другое>, и это провалилось».
1. Список тестов
Составьте список всех ожидаемых вариантов нового поведения:
- базовый случай,
- что, если время ожидания истечет,
- что, если ключа нет в БД,
и т.п.
Это поведенческий анализ. Вы думаете обо всех случаях, в которых изменение поведения должно сработать. Если вы думаете, как изменение поведения не должно нарушить существующее поведение, добавьте и это.
Ошибка: вносить детали реализации. Нет. Позже будет достаточно времени, чтобы решить, как будут выглядеть внутренности. Вы сможете лучше составить список тестов, если поведение - всё, на чём вы сосредоточитесь.
2. Напишите тест
Один. По-настоящему автоматизированный тест с настройкой, вызовом и утверждениями (совет: попробуйте начинать с утверждений). Именно сейчас вы начнёте принимать проектные решения, в первую очередь решения по интерфейсу. Некоторые решения по реализации могут просочиться, но со временем вы научитесь избегать этого.
Ошибки:
1) Писать тесты без утверждений только для того, чтобы было покрытие.
2) Писать сразу все тесты для списка, а затем заставлять их проходить по одному. Что будет, если проход 1го теста заставит вас пересмотреть решение, которое повлияет на остальные тесты? Переписывание всего, депрессия и/или скука. Выбор следующего теста — важный навык, который приходит только с опытом. Порядок тестов может существенно повлиять как на опыт программирования, так и на конечный результат.
3. Заставьте тест пройти
Измените систему так, чтобы тест прошёл успешно.
Ошибки:
1) Удалять утверждения, чтобы тест казался пройденным.
2) Копировать фактически вычисленные значения в ожидаемые значения теста. Это исключает двойную проверку, которая создает большую часть ценности TDD.
3) Смешивать рефакторинг с прохождением теста. Заставьте тест работать, а затем исправьте его. Ваш мозг (в конце концов) скажет вам спасибо.
Если в процессе вы обнаружите необходимость нового теста, добавьте его в список тестов. Если тест заставляет изменить решения и переписать предыдущие тесты, решите, продолжать или начать заново (совет: начните сначала, но выберите другой порядок реализации тестов). Когда тест пройден, вычеркните его из списка.
4. Рефакторинг
При необходимости.
Ошибки:
1) Переписывать больше, чем нужно.
2) Вводить абстракции слишком рано. Дублирование кода — это подсказка, а не приказ к действию.
5. Если список тестов не пуст, перейти к п. 2.
Продолжайте тестировать и писать код, пока ваш страх перед поведением кода не превратится в скуку.
Источник: https://tidyfirst.substack.com/p/canon-tdd
Практикуем TDD
Люди не пришли к единому мнению относительно определения процесса разработки через тестирование (TDD). Сегодня рассмотрим процесс Cannon TDD, предложенный Кентом Бэком. Если вы делаете что-то по-другому и это вам подходит, отлично!
TDD — это рабочий процесс написания ПО. Программисту необходимо изменить поведение системы (или создать её). TDD призван помочь создать новое состояние системы, в котором:
- Всё, что раньше работало, продолжает работать.
- Новое поведение работает так, как ожидалось.
- Система готова к следующим изменениям.
- Программист и его коллеги уверены в вышеперечисленных пунктах.
Разделение интерфейса/реализации
Первое недопонимание заключается в том, что люди смешивают всю разработку в одну кучу. Есть два сценария:
- Как вызывается конкретная часть поведения.
- Как система реализует такое поведение.
Шаги
Люди — паршивые компьютеры. Следующий процесс похож на алгоритм, но это не так. Он написан так в попытке эффективно общаться с людьми, привыкшими работать с программами. «Попытке», потому что люди склонны говорить: «TDD - отстой! Я сделал <совершенно другое>, и это провалилось».
1. Список тестов
Составьте список всех ожидаемых вариантов нового поведения:
- базовый случай,
- что, если время ожидания истечет,
- что, если ключа нет в БД,
и т.п.
Это поведенческий анализ. Вы думаете обо всех случаях, в которых изменение поведения должно сработать. Если вы думаете, как изменение поведения не должно нарушить существующее поведение, добавьте и это.
Ошибка: вносить детали реализации. Нет. Позже будет достаточно времени, чтобы решить, как будут выглядеть внутренности. Вы сможете лучше составить список тестов, если поведение - всё, на чём вы сосредоточитесь.
2. Напишите тест
Один. По-настоящему автоматизированный тест с настройкой, вызовом и утверждениями (совет: попробуйте начинать с утверждений). Именно сейчас вы начнёте принимать проектные решения, в первую очередь решения по интерфейсу. Некоторые решения по реализации могут просочиться, но со временем вы научитесь избегать этого.
Ошибки:
1) Писать тесты без утверждений только для того, чтобы было покрытие.
2) Писать сразу все тесты для списка, а затем заставлять их проходить по одному. Что будет, если проход 1го теста заставит вас пересмотреть решение, которое повлияет на остальные тесты? Переписывание всего, депрессия и/или скука. Выбор следующего теста — важный навык, который приходит только с опытом. Порядок тестов может существенно повлиять как на опыт программирования, так и на конечный результат.
3. Заставьте тест пройти
Измените систему так, чтобы тест прошёл успешно.
Ошибки:
1) Удалять утверждения, чтобы тест казался пройденным.
2) Копировать фактически вычисленные значения в ожидаемые значения теста. Это исключает двойную проверку, которая создает большую часть ценности TDD.
3) Смешивать рефакторинг с прохождением теста. Заставьте тест работать, а затем исправьте его. Ваш мозг (в конце концов) скажет вам спасибо.
Если в процессе вы обнаружите необходимость нового теста, добавьте его в список тестов. Если тест заставляет изменить решения и переписать предыдущие тесты, решите, продолжать или начать заново (совет: начните сначала, но выберите другой порядок реализации тестов). Когда тест пройден, вычеркните его из списка.
4. Рефакторинг
При необходимости.
Ошибки:
1) Переписывать больше, чем нужно.
2) Вводить абстракции слишком рано. Дублирование кода — это подсказка, а не приказ к действию.
5. Если список тестов не пуст, перейти к п. 2.
Продолжайте тестировать и писать код, пока ваш страх перед поведением кода не превратится в скуку.
Источник: https://tidyfirst.substack.com/p/canon-tdd
👍12👎5
День 1814. #Оффтоп
Давно не советовал вам хороших видео. Недавно наткнулся на прекрасный рассказ Enrico Tartarotti, продакт менеджера в Maze, «Ложь, которая заставляет работать технологии».
За простыми вещами, которые вы делаете каждый день и считаете само собой разумеющимися, скрывается огромный мир дизайна, инженерии, психологии, копирования природы и трюков, которые делают технологии пригодными для использования людьми. И большинство людей никогда этого не замечают.
Как на самом деле работает клавиатура в смартфоне? Сколько инженерных решений вложено в простое нажатие клавиши на экране? Не говоря уже про скроллинг или «свайп». Все мы привыкли к тому, что курсор в тексте мигает. А вы замечали, что он перестаёт мигать, когда вы набираете текст? Мы привыкли к двойному нажатию мыши на иконке, чтобы открыть. Мы привыкли к жесту разведения пальцев, чтобы увеличить что-то на экране или к двойному нажатию на пост в телефоне, чтобы поставить лайк (кстати... ну, вы поняли, что надо сделать 😉). И это кажется нам вполне естественным. Но кто научил наш мозг этим «естественным» жестам? И каким жестам нам придётся обучиться в мире дополненной реальности?
А также про многие другие решения, о которых мы даже не задумываемся, когда пользуемся привычными технологиями.
https://youtu.be/rAVV4kAYlI8
Приятного просмотра.
Давно не советовал вам хороших видео. Недавно наткнулся на прекрасный рассказ Enrico Tartarotti, продакт менеджера в Maze, «Ложь, которая заставляет работать технологии».
За простыми вещами, которые вы делаете каждый день и считаете само собой разумеющимися, скрывается огромный мир дизайна, инженерии, психологии, копирования природы и трюков, которые делают технологии пригодными для использования людьми. И большинство людей никогда этого не замечают.
Как на самом деле работает клавиатура в смартфоне? Сколько инженерных решений вложено в простое нажатие клавиши на экране? Не говоря уже про скроллинг или «свайп». Все мы привыкли к тому, что курсор в тексте мигает. А вы замечали, что он перестаёт мигать, когда вы набираете текст? Мы привыкли к двойному нажатию мыши на иконке, чтобы открыть. Мы привыкли к жесту разведения пальцев, чтобы увеличить что-то на экране или к двойному нажатию на пост в телефоне, чтобы поставить лайк (кстати... ну, вы поняли, что надо сделать 😉). И это кажется нам вполне естественным. Но кто научил наш мозг этим «естественным» жестам? И каким жестам нам придётся обучиться в мире дополненной реальности?
А также про многие другие решения, о которых мы даже не задумываемся, когда пользуемся привычными технологиями.
https://youtu.be/rAVV4kAYlI8
Приятного просмотра.
YouTube
The LIES That Make Your Tech ACTUALLY Work
Learn EXACTLY how I make my videos:
https://www.enricotartarotti.com/storybehind?utm_source=desc
Instagram: https://www.instagram.com/thisisenri
--------
📮 Behind the scenes and nuggets on my free newsletter:
https://www.enricotartarotti.com/email-club…
https://www.enricotartarotti.com/storybehind?utm_source=desc
Instagram: https://www.instagram.com/thisisenri
--------
📮 Behind the scenes and nuggets on my free newsletter:
https://www.enricotartarotti.com/email-club…
👍18👎1
День 1815. #Карьера
Забота – это Больше, чем Долгие Часы Работы
Бывший инженер Netflix Алекс Кастильо в твите размышляет о ценности заботы о результате. Их команда в пятницу обнаружила неработающее приложение и сумела всё исправить к понедельнику. Быстрый результат объясняется тем, насколько «заботились о продукте» вовлеченные люди.
Приложение, унаследованное командой Алекса, они не смогли даже запустить локально, не говоря уже о том, чтобы исправить в пятницу. В понедельник они решили собраться, чтобы найти решение. Алекс, стремясь проявить себя, в выходные написал новое приложение для замены. Он представил его своему менеджеру, но тот поделился с ним приложением, которое другой член команды создал за выходные. Развернули второе предложение (скорее всего, собравшись и сравнив варианты). Алекс пишет: «Именно тогда я понял, что я работаю с неравнодушными людьми и хочу работать только с такими людьми». И он прав.
Когда все в команде преданы цели, их результаты превосходят сумму их индивидуальных способностей. Но нельзя приравнивать работу в выходные к заботе!
Мы знаем историю только по короткому твиту и не в курсе деталей. Но твит сочетает в себе положительную ценность засучивания рукавов для поиска решений неотложных проблем с переутомлением и геройским менталитетом, что опасно. Есть разница между заботой, бросаться с головой в проблему, и заботой, сделать шаг назад и все тщательно обдумать.
Долгий рабочий день — плохая замена хорошей инженерии
Иногда надо поработать на выходных. Похоже, команда Алекса оказалась в такой ситуации. Их усилия помогли решить проблему, что наверняка принесло пользу бизнесу.
Но. Два инженера в команде из трёх провели выходные за работой. Насколько хорошо команда сможет выступить на этой неделе? Что, если бы новое приложение оказалось неисправным или возник бы другой, более серьёзный сбой? Работа сверхурочно — эффективный способ потушить пожар. Но это неустойчивый подход, и его не следует поощрять как способ проявить заботу.
Работа в выходные — это решение симптомов, которое не устраняет основную проблему. Исправлять сломанное приложение бессмысленно, если вы не устраните проблемы, которые изначально привели к поломке. Если не провести анализ для выявления основной причины проблемы и способов её предотвращения в будущем, это приведёт лишь к созданию прецедента, который другие могут счесть нормальностью.
Лучший подход — постоянно концентрироваться на качестве. Проявите заботу через результаты труда, а не через количество рабочих часов. Забота — это выполнение скромной, скучной, но необходимой работы по очистке, которую требует каждая устоявшаяся кодовая база. Иногда приходится сопротивляться давлению, требованию новых фич, чтобы обеспечить надёжную реализацию.
Общение – это забота
Ещё одна проблема в истории Алекса - отсутствие общения. Почему он или его коллега не договорились? Они могли бы поработать вместе и достичь лучшего результата. А менеджер мог бы либо уговорить их отдохнуть, либо подбодрить и компенсировать им работу в выходные отгулом на неделе.
Коммуникация является важнейшим компонентом эффективной и продуманной командной работы.
Проявляйте заботу последовательно, а не интенсивно
Третий член команды, не представивший своё решение, ему разве было всё равно? Возможно, но этот случай не показатель. Хорошо работать только с людьми, которым не всё равно, но в первую очередь нужно заботиться о долгосрочной игре. Важно расслабляться, чтобы сосредоточиться во время работы и придумывать новые решения, которые может дать только отдохнувший мозг.
Алекс хотел подчеркнуть ценную черту, но выбрал для этого не ту историю. Он и его коллега проявили большую заботу, но не потому, что работали в выходные. Они взяли на себя ответственность и инициативу и принялись за решение насущной проблемы. Ищите людей, которые возьмут на себя ответственность за проблемы и посвятят себя их решению. Но не путайте переработку с заботой.
Источник: https://betterprogramming.pub/theres-more-to-caring-than-working-long-hours-f64a89e9c718
Забота – это Больше, чем Долгие Часы Работы
Бывший инженер Netflix Алекс Кастильо в твите размышляет о ценности заботы о результате. Их команда в пятницу обнаружила неработающее приложение и сумела всё исправить к понедельнику. Быстрый результат объясняется тем, насколько «заботились о продукте» вовлеченные люди.
Приложение, унаследованное командой Алекса, они не смогли даже запустить локально, не говоря уже о том, чтобы исправить в пятницу. В понедельник они решили собраться, чтобы найти решение. Алекс, стремясь проявить себя, в выходные написал новое приложение для замены. Он представил его своему менеджеру, но тот поделился с ним приложением, которое другой член команды создал за выходные. Развернули второе предложение (скорее всего, собравшись и сравнив варианты). Алекс пишет: «Именно тогда я понял, что я работаю с неравнодушными людьми и хочу работать только с такими людьми». И он прав.
Когда все в команде преданы цели, их результаты превосходят сумму их индивидуальных способностей. Но нельзя приравнивать работу в выходные к заботе!
Мы знаем историю только по короткому твиту и не в курсе деталей. Но твит сочетает в себе положительную ценность засучивания рукавов для поиска решений неотложных проблем с переутомлением и геройским менталитетом, что опасно. Есть разница между заботой, бросаться с головой в проблему, и заботой, сделать шаг назад и все тщательно обдумать.
Долгий рабочий день — плохая замена хорошей инженерии
Иногда надо поработать на выходных. Похоже, команда Алекса оказалась в такой ситуации. Их усилия помогли решить проблему, что наверняка принесло пользу бизнесу.
Но. Два инженера в команде из трёх провели выходные за работой. Насколько хорошо команда сможет выступить на этой неделе? Что, если бы новое приложение оказалось неисправным или возник бы другой, более серьёзный сбой? Работа сверхурочно — эффективный способ потушить пожар. Но это неустойчивый подход, и его не следует поощрять как способ проявить заботу.
Работа в выходные — это решение симптомов, которое не устраняет основную проблему. Исправлять сломанное приложение бессмысленно, если вы не устраните проблемы, которые изначально привели к поломке. Если не провести анализ для выявления основной причины проблемы и способов её предотвращения в будущем, это приведёт лишь к созданию прецедента, который другие могут счесть нормальностью.
Лучший подход — постоянно концентрироваться на качестве. Проявите заботу через результаты труда, а не через количество рабочих часов. Забота — это выполнение скромной, скучной, но необходимой работы по очистке, которую требует каждая устоявшаяся кодовая база. Иногда приходится сопротивляться давлению, требованию новых фич, чтобы обеспечить надёжную реализацию.
Общение – это забота
Ещё одна проблема в истории Алекса - отсутствие общения. Почему он или его коллега не договорились? Они могли бы поработать вместе и достичь лучшего результата. А менеджер мог бы либо уговорить их отдохнуть, либо подбодрить и компенсировать им работу в выходные отгулом на неделе.
Коммуникация является важнейшим компонентом эффективной и продуманной командной работы.
Проявляйте заботу последовательно, а не интенсивно
Третий член команды, не представивший своё решение, ему разве было всё равно? Возможно, но этот случай не показатель. Хорошо работать только с людьми, которым не всё равно, но в первую очередь нужно заботиться о долгосрочной игре. Важно расслабляться, чтобы сосредоточиться во время работы и придумывать новые решения, которые может дать только отдохнувший мозг.
Алекс хотел подчеркнуть ценную черту, но выбрал для этого не ту историю. Он и его коллега проявили большую заботу, но не потому, что работали в выходные. Они взяли на себя ответственность и инициативу и принялись за решение насущной проблемы. Ищите людей, которые возьмут на себя ответственность за проблемы и посвятят себя их решению. Но не путайте переработку с заботой.
Источник: https://betterprogramming.pub/theres-more-to-caring-than-working-long-hours-f64a89e9c718
👍12
День 1817. #ЧтоНовенького #CSharp13
Новый Тип Блокировки в .NET 9.
Продолжаем заглядывать в будущее. Новый тип System.Threading.Lock, представлен для .NET 9.
Предупреждение: тип Lock всё ещё находится в режиме предварительного просмотра. Вы можете загрузить текущую сборку .NET 9 и выбрать новый тип блокировки с помощью переключателя
в файле csproj. Тем не менее, в будущем в этом типе ещё могут произойти кардинальные изменения.
Новый тип блокировки имеет одну основную цель: быть блокировкой. Т.е. представляет собой блокировку и только блокировку. Нет никакой двусмысленности в том, что он делает. До сих пор мы сделали что-то вроде следующего:
Новый тип Lock выражает это более явно:
Есть и другое предложение: "Паттерн оператора блокировки". Идея в том, чтобы иметь маркерный интерфейс ILockPattern, который «специально обрабатывается» компилятором и несколько новых типов блокировок. Например, следующий код:
Будет преобразовано в:
Здесь _lock может быть любым типом блокировки, реализующим ILockPattern. EnterLockScope будет выдавать disposable контекст блокировки.
Источник: https://steven-giesel.com/blogPost/d7f923b3-13ff-4ecc-8b8f-d847ae581f68/a-new-lock-type-in-net-9
Новый Тип Блокировки в .NET 9.
Продолжаем заглядывать в будущее. Новый тип System.Threading.Lock, представлен для .NET 9.
Предупреждение: тип Lock всё ещё находится в режиме предварительного просмотра. Вы можете загрузить текущую сборку .NET 9 и выбрать новый тип блокировки с помощью переключателя
<EnablePreviewFeatures>true</EnablePreviewFeatures>
в файле csproj. Тем не менее, в будущем в этом типе ещё могут произойти кардинальные изменения.
Новый тип блокировки имеет одну основную цель: быть блокировкой. Т.е. представляет собой блокировку и только блокировку. Нет никакой двусмысленности в том, что он делает. До сих пор мы сделали что-то вроде следующего:
object _obj = new();
void DoSomething()
{
lock (_obj)
{
// Что-то делаем
}
}
Новый тип Lock выражает это более явно:
Lock _lock = new();Использование его абсолютно такое же (на данный момент). Но новый тип явный и теоретически может быть быстрее. Это зависит от используемой базовой реализации. До сих пор конструкция lock { … } всегда заменялась на пару операторов Monitor.Enter/Monitor.Exit, но теперь это не обязательно будет так.
void DoSomething()
{
lock (_lock)
{
// Что-то делаем
}
}
Есть и другое предложение: "Паттерн оператора блокировки". Идея в том, чтобы иметь маркерный интерфейс ILockPattern, который «специально обрабатывается» компилятором и несколько новых типов блокировок. Например, следующий код:
class MyDataStructure
{
private Lock _lock = new();
void Foo()
{
lock (_lock)
{
// Что-то делаем
}
}
}
Будет преобразовано в:
class MyDataStructure
{
private Lock _lock = new();
void Foo()
{
using (_lock.EnterLockScope())
{
// Что-то делаем
}
}
}
Здесь _lock может быть любым типом блокировки, реализующим ILockPattern. EnterLockScope будет выдавать disposable контекст блокировки.
Источник: https://steven-giesel.com/blogPost/d7f923b3-13ff-4ecc-8b8f-d847ae581f68/a-new-lock-type-in-net-9
👍24
День 1818. #ЗаметкиНаПолях
Создаём Защитные Предложения
Защитные предложения (Guard Clause) в .NET — это простые проверки данных или условий в начале функции или метода.
Преимущества:
- уменьшают сложность и повышают читаемость, позволяя избегать глубокой вложенности кода;
- повышают безопасность и надёжность кода, проверяя входные данные или условия в начале, предотвращая потенциальные ошибки во время выполнения или непредвиденное поведение;
- проясняют цель кода, явно указывая предварительные условия, необходимые для правильного выполнения последующего кода.
Обычно проверка входных данных выглядит примерно так:
Здесь всё просто. Спорный вопрос про выброс исключения в случае невалидных данных, но сейчас не об этом. Что здесь не так? В принципе, всё нормально, кроме читаемости кода. А что, если проверок будет больше?
Создадим статический метод для проверки условий.
Здесь вместо того, чтобы постоянно передавать имя параметра, для которого выдаётся исключение, мы используем атрибут CallerArgumentExpression, передавая ему имя нужного параметра. Во время выполнения это значение будет прочитано и присвоено атрибуту paramName.
Теперь работа по проверке условий и выбросу исключений ложится на отдельный класс, а код проверяемого класса проще:
Многие разработчики уже создали качественные библиотеки для этой же цели.
Вот некоторые из них:
- Ardalis
- Dawn
- Throw
Какую бы вы ни выбрали, у вас не возникнет никаких проблем. А если вам хочется большего контроля, вы всегда можете создать собственное защитное предложение, как показано выше.
Источник: https://stefandjokic.tech/posts/how-to-create-dotnet-custom-guard-clause
Создаём Защитные Предложения
Защитные предложения (Guard Clause) в .NET — это простые проверки данных или условий в начале функции или метода.
Преимущества:
- уменьшают сложность и повышают читаемость, позволяя избегать глубокой вложенности кода;
- повышают безопасность и надёжность кода, проверяя входные данные или условия в начале, предотвращая потенциальные ошибки во время выполнения или непредвиденное поведение;
- проясняют цель кода, явно указывая предварительные условия, необходимые для правильного выполнения последующего кода.
Обычно проверка входных данных выглядит примерно так:
public class Person
{
public string Name { get; private set; }
public Person(string name)
{
if (string.IsNullOrEmpty(name))
throw new ArgumentException(
"Имя не может быть пустым.",
nameof(name));
Name = name;
}
}
Здесь всё просто. Спорный вопрос про выброс исключения в случае невалидных данных, но сейчас не об этом. Что здесь не так? В принципе, всё нормально, кроме читаемости кода. А что, если проверок будет больше?
Создадим статический метод для проверки условий.
public static class Ensure
{
public static void NotNullOrEmpty(
string? value,
[CallerArgumentExpression("value")]
string? paramName = null)
{
if(string.IsNullOrEmpty(value))
throw new ArgumentException(
"Значение не может быть пустым",
paramName);
}
}
Здесь вместо того, чтобы постоянно передавать имя параметра, для которого выдаётся исключение, мы используем атрибут CallerArgumentExpression, передавая ему имя нужного параметра. Во время выполнения это значение будет прочитано и присвоено атрибуту paramName.
Теперь работа по проверке условий и выбросу исключений ложится на отдельный класс, а код проверяемого класса проще:
public Person(string name)
{
Ensure.NotNullOrEmpty(name);
//…
}
Многие разработчики уже создали качественные библиотеки для этой же цели.
Вот некоторые из них:
- Ardalis
- Dawn
- Throw
Какую бы вы ни выбрали, у вас не возникнет никаких проблем. А если вам хочется большего контроля, вы всегда можете создать собственное защитное предложение, как показано выше.
Источник: https://stefandjokic.tech/posts/how-to-create-dotnet-custom-guard-clause
👍24
День 1819. #ЗаметкиНаПолях
Непостоянные Строковые Константы
Ключевое слово const работает следующим образом: компилятор помещает строку в блок памяти только для чтения, а затем заменяет все ссылки на строку адресом строки в памяти. Поэтому следующий код:
Будет преобразован в:
В скомпилированном коде нет foo. Поэтому нельзя использовать в константах переменные или вещи, которые «неизвестны» во время компиляции. Но можем ли мы изменить константную строку?
Интернирование строк
Где-то в памяти должна храниться строка. Используемый здесь механизм называется интернированием строк. Компилятор проверяет все константные строки в коде и помещает их в специальную таблицу в памяти. Если у вас есть две константные строки с одинаковым значением, они будут помещены в одну запись таблицы. Поэтому код:
Будет преобразован в:
… и выведет True.
Компилятор заменит все ссылки на foo и other_foo строкой «bar», а поскольку строки интернированы, они будут равны.
Теперь попробуем изменить запись в таблице интернированных строк. Итак, по логике, даже после вызова ModifyString, на консоль должно быть выведено
Как это изменить? Просто получим адрес строки в памяти и изменим то, что там хранится:
В итоге в выводе получим
Самые внимательные заметили, что мы записали больше символов, чем длина строки. Компилятор поместит строку в буфер с длиной строки + 1. Поэтому, если мы напишем больше, чем длина строки, мы перезапишем следующую строку в буфере. Выводится всё равно Foo, поскольку метаданные объекта сохраняют его длину, поэтому длина нашей строки по-прежнему составляет 3 символа. Вы можете подумать: а что, если в следующем блоке памяти есть что-нибудь, могу ли я там что-то перезаписать? Короткий ответ: нет, вы получите исключение System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt. (Попытка чтения или записи защищенной памяти». Часто это указывает на то, что другая память повреждена.)
Напоследок ещё один момент, касающийся интернирования строк. Если в конец предыдущего примера мы добавим вывод строки, построенной следующим образом:
Как вы думаете, что будет выведено на консоль:
Будет выведено
Итого
Мы увидели, что константные строки в конце концов не такие уж и константные (не без костылей, но всё же). Вам, скорее всего (надеюсь), это не пригодится, но понимание внутренностей может быть интересным и полезным! Вот ссылка на Sharplab.io, если вы хотите напрямую поиграть с кодом.
Источник: https://steven-giesel.com/blogPost/544c33da-be2e-4d96-99c9-9313f48548bd/const-strings-are-not-so-const-after-all
Непостоянные Строковые Константы
Ключевое слово const работает следующим образом: компилятор помещает строку в блок памяти только для чтения, а затем заменяет все ссылки на строку адресом строки в памяти. Поэтому следующий код:
const string foo = "bar";
Console.WriteLine(foo);
Console.WriteLine(foo);
Будет преобразован в:
Console.WriteLine("bar");
Console.WriteLine("bar");
В скомпилированном коде нет foo. Поэтому нельзя использовать в константах переменные или вещи, которые «неизвестны» во время компиляции. Но можем ли мы изменить константную строку?
Интернирование строк
Где-то в памяти должна храниться строка. Используемый здесь механизм называется интернированием строк. Компилятор проверяет все константные строки в коде и помещает их в специальную таблицу в памяти. Если у вас есть две константные строки с одинаковым значением, они будут помещены в одну запись таблицы. Поэтому код:
const string foo = "bar";
const string other_foo = "bar";
Console.WriteLine(foo);
Console.WriteLine(other_foo);
Console.WriteLine(ReferenceEquals(foo, other_foo));
Будет преобразован в:
Console.WriteLine("bar");
Console.WriteLine("bar");
Console.WriteLine((object)"bar" == "bar");
… и выведет True.
Компилятор заменит все ссылки на foo и other_foo строкой «bar», а поскольку строки интернированы, они будут равны.
Теперь попробуем изменить запись в таблице интернированных строк. Итак, по логике, даже после вызова ModifyString, на консоль должно быть выведено
bar
:const string foo = "bar";
ModifyString(foo);
Console.WriteLine(foo);
Как это изменить? Просто получим адрес строки в памяти и изменим то, что там хранится:
unsafe void ModifyString(string foo)
{
fixed(char* f = foo)
{
f[0] = 'F';
f[1] = 'o';
f[2] = 'o';
f[3] = '!';
}
}
В итоге в выводе получим
Foo
. Мы не можем изменить строку (в смысле ее адреса), но можем изменить её содержимое. Интересно, что это также сработает и для переменной var foo = "bar"
.Самые внимательные заметили, что мы записали больше символов, чем длина строки. Компилятор поместит строку в буфер с длиной строки + 1. Поэтому, если мы напишем больше, чем длина строки, мы перезапишем следующую строку в буфере. Выводится всё равно Foo, поскольку метаданные объекта сохраняют его длину, поэтому длина нашей строки по-прежнему составляет 3 символа. Вы можете подумать: а что, если в следующем блоке памяти есть что-нибудь, могу ли я там что-то перезаписать? Короткий ответ: нет, вы получите исключение System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt. (Попытка чтения или записи защищенной памяти». Часто это указывает на то, что другая память повреждена.)
Напоследок ещё один момент, касающийся интернирования строк. Если в конец предыдущего примера мы добавим вывод строки, построенной следующим образом:
Console.WriteLine(string.Concat("b", "ar"));
Как вы думаете, что будет выведено на консоль:
Foo
или bar
? Будет выведено
bar
, поскольку «динамические» строки не интернируются, и поэтому в нашем случае строка, созданная из string.Concat, находится по другому адресу в памяти.Итого
Мы увидели, что константные строки в конце концов не такие уж и константные (не без костылей, но всё же). Вам, скорее всего (надеюсь), это не пригодится, но понимание внутренностей может быть интересным и полезным! Вот ссылка на Sharplab.io, если вы хотите напрямую поиграть с кодом.
Источник: https://steven-giesel.com/blogPost/544c33da-be2e-4d96-99c9-9313f48548bd/const-strings-are-not-so-const-after-all
👍27👎1
День 1820. #ЧтоНовенького
NuGetSolver: Разрешение Конфликтов Зависимостей в Visual Studio
Microsoft представили новый экспериментальный инструмент NuGetSolver. Это расширение Visual Studio было создано совместно с Microsoft Research. Цель — упростить разрешение конфликтов зависимостей NuGet в проектах Visual Studio. Расширение эффективно устраняет распространённые ошибки и предупреждения NuGet, такие как ограничения зависимостей между пакетами (NU1107), случаи, когда пакет зависимостей не содержит ресурсов, совместимых с проектом (NU1202), обнаруженные понижения версии пакета (NU1605), и предупреждения о несовместимости версий (NU1701).
Чтобы начать использовать NuGetSolver, разработчики могут загрузить расширение из Visual Studio Marketplace. Расширение предлагает решение исследуя все зависимости и предоставляя интеллектуальные и автоматизированные предложения. Доступ к этому инструменту можно получить через обозреватель решений, щёлкнув правой кнопкой мыши и выбрав Resolve Dependency Conflicts (Разрешить конфликты зависимостей). Инструмент отобразит различия между текущим и предлагаемым состояниями. Пользователи также могут включить флажок Show only changes (Показать только изменения), чтобы просмотреть полный список зависимостей. По умолчанию инструмент предлагает стабильные версии с возможностью включения предварительных версий при необходимости. Если рекомендуемое предложение приемлемо для разработчика, нужно нажать кнопку Apply fix (Применить исправление) и пересобрать решение.
Создатели рекомендуют включить решение в систему контроля версий, чтобы при необходимости можно было легко отменить любые изменения, внесённые NuGetSolver.
Если в решении в настоящее время отсутствуют конфликты зависимостей, разработчики всё равно могут запустить инструмент для эффективного обновления зависимостей, минимизируя изменения. Это оказывается более быстрой и надёжной альтернативой обновлению пакетов по одному с помощью менеджера пакетов NuGet.
Однако существуют некоторые рекомендации по использованию и известные ограничения:
1. В настоящее время поддерживается только канал nuget.org.
2. Исправление может не полностью поддерживать обновление версий, если для настроек версии используется пользовательская логика MSBuild.
3. NuGetSolver может не учитывать все доступные предварительные версии во время расчёта предложений.
4. Для проектов, использующих packages.config или устаревшие sdk, инструмент может генерировать предложения, но не может применить исправление автоматически, требуется исправление вручную.
5. Хотя NuGetSolver может разрешать конфликты зависимостей во время компиляции, ошибки во время выполнения все равно могут возникать.
6. Инструмент не проверяет наличие известных уязвимостей в предлагаемых версиях, поэтому разработчикам рекомендуется использовать функцию аудита в NuGet для решения этой проблемы.
Источник: https://www.infoq.com/news/2024/01/introducing-nuget-solver/
NuGetSolver: Разрешение Конфликтов Зависимостей в Visual Studio
Microsoft представили новый экспериментальный инструмент NuGetSolver. Это расширение Visual Studio было создано совместно с Microsoft Research. Цель — упростить разрешение конфликтов зависимостей NuGet в проектах Visual Studio. Расширение эффективно устраняет распространённые ошибки и предупреждения NuGet, такие как ограничения зависимостей между пакетами (NU1107), случаи, когда пакет зависимостей не содержит ресурсов, совместимых с проектом (NU1202), обнаруженные понижения версии пакета (NU1605), и предупреждения о несовместимости версий (NU1701).
Чтобы начать использовать NuGetSolver, разработчики могут загрузить расширение из Visual Studio Marketplace. Расширение предлагает решение исследуя все зависимости и предоставляя интеллектуальные и автоматизированные предложения. Доступ к этому инструменту можно получить через обозреватель решений, щёлкнув правой кнопкой мыши и выбрав Resolve Dependency Conflicts (Разрешить конфликты зависимостей). Инструмент отобразит различия между текущим и предлагаемым состояниями. Пользователи также могут включить флажок Show only changes (Показать только изменения), чтобы просмотреть полный список зависимостей. По умолчанию инструмент предлагает стабильные версии с возможностью включения предварительных версий при необходимости. Если рекомендуемое предложение приемлемо для разработчика, нужно нажать кнопку Apply fix (Применить исправление) и пересобрать решение.
Создатели рекомендуют включить решение в систему контроля версий, чтобы при необходимости можно было легко отменить любые изменения, внесённые NuGetSolver.
Если в решении в настоящее время отсутствуют конфликты зависимостей, разработчики всё равно могут запустить инструмент для эффективного обновления зависимостей, минимизируя изменения. Это оказывается более быстрой и надёжной альтернативой обновлению пакетов по одному с помощью менеджера пакетов NuGet.
Однако существуют некоторые рекомендации по использованию и известные ограничения:
1. В настоящее время поддерживается только канал nuget.org.
2. Исправление может не полностью поддерживать обновление версий, если для настроек версии используется пользовательская логика MSBuild.
3. NuGetSolver может не учитывать все доступные предварительные версии во время расчёта предложений.
4. Для проектов, использующих packages.config или устаревшие sdk, инструмент может генерировать предложения, но не может применить исправление автоматически, требуется исправление вручную.
5. Хотя NuGetSolver может разрешать конфликты зависимостей во время компиляции, ошибки во время выполнения все равно могут возникать.
6. Инструмент не проверяет наличие известных уязвимостей в предлагаемых версиях, поэтому разработчикам рекомендуется использовать функцию аудита в NuGet для решения этой проблемы.
Источник: https://www.infoq.com/news/2024/01/introducing-nuget-solver/
👍11
День 1821. #ЗаметкиНаПолях
Разница Mежду CultureInfo.Get и new CultureInfo
Если вы хотите получить объект CultureInfo, вы можете использовать статический метод CultureInfo.GetCultureInfo или конструктор класса CultureInfo. Рассмотрим разницу между ними.
Конструктор создает новый экземпляр указанной культуры. Вы можете редактировать данные этого экземпляра. Это означает, что вы можете изменить календарь, числовой формат и т. д. Следующий код показывает, как изменить символ валюты:
Статический метод CultureInfo.GetCultureInfo возвращает доступный только для чтения экземпляр указанной культуры. Вы не можете редактировать данные этого экземпляра. И поэтому метод может кэшировать экземпляр. Таким образом, это может быть быстрее и уменьшить использование памяти.
Если выполнить простой тест производительности, можно увидеть разницу в распределении между двумя методами:
Итого
Используйте CultureInfo.GetCultureInfo большую часть времени, поскольку редактирование объекта CultureInfo требуется очень редко.
Замечание: GetCultureInfo возвращает экземпляр культуры, доступный только для чтения, однако компилятор не запретит вам изменять его свойства и не выдаст ошибку компиляции. В этом случае при попытке изменить свойства такого экземпляра возникнет ошибка времени выполнения.
Источник: https://www.meziantou.net/difference-between-cultureinfo-get-and-new-cultureinfo.htm
Разница Mежду CultureInfo.Get и new CultureInfo
Если вы хотите получить объект CultureInfo, вы можете использовать статический метод CultureInfo.GetCultureInfo или конструктор класса CultureInfo. Рассмотрим разницу между ними.
var c1 = CultureInfo.GetCultureInfo("en-US");
var c2 = new CultureInfo("en-US");
Конструктор создает новый экземпляр указанной культуры. Вы можете редактировать данные этого экземпляра. Это означает, что вы можете изменить календарь, числовой формат и т. д. Следующий код показывает, как изменить символ валюты:
var c = new CultureInfo("en-US");
c.NumberFormat.CurrencySymbol = "€";
Console.WriteLine(42.ToString("C", c));
// €42.00
Статический метод CultureInfo.GetCultureInfo возвращает доступный только для чтения экземпляр указанной культуры. Вы не можете редактировать данные этого экземпляра. И поэтому метод может кэшировать экземпляр. Таким образом, это может быть быстрее и уменьшить использование памяти.
var c1 = CultureInfo.GetCultureInfo("en-US");
var c2 = CultureInfo.GetCultureInfo("en-US");
Console.WriteLine(ReferenceEquals(c1, c2));
// True
Если выполнить простой тест производительности, можно увидеть разницу в распределении между двумя методами:
[MemoryDiagnoser]
public class CultureInfoBenchmark
{
[Benchmark(Baseline = true)]
public void Ctor()
{
_ = new CultureInfo("en-US");
}
[Benchmark]
public void StaticMethod()
{
_ = CultureInfo.GetCultureInfo("en-US");
}
}
Method Mean Allocated
Ctor 33.74 ns 144 B
StaticMethod 24.44 ns 32 B
Итого
Используйте CultureInfo.GetCultureInfo большую часть времени, поскольку редактирование объекта CultureInfo требуется очень редко.
Замечание: GetCultureInfo возвращает экземпляр культуры, доступный только для чтения, однако компилятор не запретит вам изменять его свойства и не выдаст ошибку компиляции. В этом случае при попытке изменить свойства такого экземпляра возникнет ошибка времени выполнения.
Источник: https://www.meziantou.net/difference-between-cultureinfo-get-and-new-cultureinfo.htm
👍13