5 Малоизвестных Функций C#, Которые Упростят Вашу Жизнь
Фреймворк уже имеет надёжные инструменты для решения различных проблем, но многие из них мало известны. Сегодня рассмотрим некоторые.
1. OperatingSystem.IsX вместо RuntimeInformation
В течение многих лет проверка текущей ОС в .NET означала написание чего-то вроде:
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
//…
}
Теперь появились более чистые варианты:
- OperatingSystem.IsWindows,
- OperatingSystem.IsLinux,
- OperatingSystem.IsMacOS
и т.п.
if (OperatingSystem.IsWindows())
{
//…
}
2. Правильная изоляция плагинов с помощью AssemblyLoadContext
Если вы когда-либо пытались создать систему плагинов в .NET, вы знаете, насколько болезненны конфликты сборок. Загрузите две версии одной и той же зависимости, и внезапно всё перестаёт работать. AssemblyLoadContext позволяет загружать сборки изолированно, так что два плагина могут зависеть от разных версий одной и той же DLL, не мешая друг другу. Вот минимальный код для начала:
class PluginLoadContext : AssemblyLoadContext
{
private AssemblyDependencyResolver _resolver;
public PluginLoadContext(string path) =>
_resolver = new(path);
protected override Assembly? Load(AssemblyName name)
{
var path = _resolver
.ResolveAssemblyToPath(name);
return path != null
? LoadFromAssemblyPath(path)
: null;
}
}
Теперь каждый плагин существует в своей среде. Вы также можете их выгружать, что очень важно для длительно работающих процессов, таких как серверы.
3. Разбор зависимостей с помощью AssemblyDependencyResolver
AssemblyDependencyResolver, получив путь к плагину или сборке, определяет, откуда должны браться зависимости:
var resolver =
new AssemblyDependencyResolver(pluginPath);
var path = resolver
.ResolveAssemblyToPath(assemblyName);
Вы получаете правильные правила разрешения зависимостей, соответствующие тому, как это обычно делает .NET, но с областью действия в контексте плагина. Так вы избегаете неприятных сюрпризов, когда плагин ссылается на Newtonsoft.Json 13.0.1, а хост-приложение использует 12.0.3.
4. Получение версий сборок без ошибок
Часто в коде можно встретить что-то такое:
var version = Assembly
.GetExecutingAssembly()
.GetName()
.Version;
Дело в том, что это значение не всегда совпадает с версией, которую вы указали в проекте. Это версия сборки, а не обязательно версия файла или информационная версия. Какая из них вам нужна?
-
AssemblyName.Version - версия сборки, полученная во время компиляции,-
FileVersionInfo.GetVersionInfo(assembly.Location).FileVersion - версия файла,-
AssemblyInformationalVersionAttribute - семантическая версия, вроде 1.0.0-beta+sha.abc123.Лучший подход – выражаться более явно:
var version = Assembly
.GetExecutingAssembly()
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?
.InformationalVersion;
Так вы получите именно ту строку, которую хотели передать, а не то, что MSBuild по умолчанию присвоил вашей DLL.
5. Получение зависимостей из контейнера с помощью ActivatorUtilities
Иногда нужно создать объект, который не зарегистрирован в контейнере, но имеет зависимости, которые зарегистрированы. Используйте ActivatorUtilities:
var myService = ActivatorUtilities
.CreateInstance<MyService>(serviceProvider);
Это говорит контейнеру: «Я знаю, что MyService не зарегистрирован, но, пожалуйста, внедри все необходимые ему сервисы». Это удобный обходной путь, который избавляет вас от необходимости создавать неудобные фабрики или засорять код логикой разрешения сервисов.
Источник: https://blog.stackademic.com/if-youre-not-using-these-5-net-features-you-re-working-too-hard-0aefbf5a6fdc?gi=bb3ac273e638