@csharp_ci
Please open Telegram to view this post
VIEW IN TELEGRAM
@csharp_ci
Please open Telegram to view this post
VIEW IN TELEGRAM
Здесь и сборники рецептов, и теория по сетевому программированию, и паттерны проектирования приложений, и описание принципов работы асинхронного кода
Даже ссылки на обзоры IDE и инструменты для работы с C#
Рекомендую)
@csharp_ci
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Начнём с того, что
event
и delegate
— это 2 абсолютно разных вещи. Разница между полем-делегатом и event'ом примерно такая же, как между полем и свойством: event иногда выглядит как делегатное поле. Давайте разберёмся в этом.delegate
— это класс, содержащий в себе «шаблон» метода, то есть, сигнатуру метода. Переменная делегатного типа — объект типа MulticastDelegate
(точнее, производного от него), который может содержать один или несколько объектов, представляющих собой методы с совместимой с «шаблоном» сигнатурой (контр- и ковариантность немного усложняет картину). То есть это как бы переменная, которая может содержать функции. Для таких переменных определена операция +
, которая комбинирует слагаемые-функции в одну новую функцию, и симметричная операция -
. Эти операции автоматически порождают производные операции +=
и -=
.event
же — это просто пара методов в классе, обозначаемых как add
и remove
, и имеющих произвольную семантику, выбранную программистом. (Аналог — геттер и сеттер свойства.) В имплементации по умолчанию для event'а заводится скрытое поле делегатного типа, а add/remove добавляют или убирают из него методы (под lock'ом). (Чтобы немного запутать картину, это скрытое поле доступно по тому же имени, что и event
.) Функции add
/remove
, составляющие event
, *вызываются* соответственно как +=
и -=
. Никаких операций +
/-
, разумеется, нету.@csharp_ci
Please open Telegram to view this post
VIEW IN TELEGRAM
Держите очень полезную roadmap от Михаила Флёнова, автора «Библия C#»
Это подборка видео и статей, которые помогут на пути C# Web разработчика
Пользуйтесь)
@csharp_ci
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Что выведет на экран этот код?
Anonymous Quiz
24%
10 10
45%
10 20
31%
Возникнет ошибка времени выполнения
null
или пустую коллекцию? Есть ли общепринятая практика?На запрос «дайте мне список всех пользователей» пустая коллекция имеет очевидный смысл: «окей, вот вам список всех пользователей, их ровно 0 штук».
С другой стороны, возвращённый
null
может означать что угодно: «я не знаю, сколько пользователей», «количество пользователей ещё не подсчитано», «текущая база данных вообще не имеет понятия пользователь», что угодно.То есть смысл пустой коллекции всегда ясен, смысл
null
не определён.null
— так вы избавите пользователя от необходимости писать утомительный код проверок на null
и, соответственно, избавите его от большого количества NullReferenceException
, если он таковой код написать забыл. Хорошим примером могут быть случаи, когда коллекции задействованы в цепочках методов видаvar res = FirstMethod().SecondMethod().ThirdMethod();
и в случае, если коллекция равна
null
, вся цепочка может быть обрушена исключением, тогда как проверки на null
превратят этот лаконичный и довольно элегантный синтаксис в кашу из вложенных if
-ов. Для коллекций это можно считать общепринятой практикой.Кстати, в LINQ именно так и делается. Например, следующий код вполне нормально работает без выбрасывания исключений, хотя очевидно, что ни один из элементов коллекции не удовлетворяет условию лямбды в
Where
:var res2 = new[] { 1, 2, 3 }.Where(x => x > 10).Select(x => x);
@csharp_ci
Please open Telegram to view this post
VIEW IN TELEGRAM
Если вы хотите глубже разобраться, как работать с элементами ProgressBar и BackgroundWorker в C#, с элементом FolderBrowserDialog, а также с событиями DoWork, ProgressChanged, RunWorkerCompleted, то большая часть этой статьи может принести вам много нового опыта.
Уверен, будет полезно)
Будущее приложение по поиску файлов будет выглядеть как на изображении
@csharp_ci
Please open Telegram to view this post
VIEW IN TELEGRAM
out
даёт такой же результат, как и с ref
.Такой код:
private void func(out string value)
{
value = "Hello World!";
}
Даёт такой же эффект, как и:
private void func(ref string value)
{
value = "Hello World!";
}
В чём же разница между
out
и ref
?out
— это выходной параметр, а ref
— входно-выходной.Для
ref
-параметра вы должны передать его инициализированным, и можете пользоваться его исходным значением. А для out
-параметра вы не обязаны инициализировать его перед вызовом функции, не можете использовать его значение в функции до присваивания, и обязаны инициализировать его в функции.(Таким образом,
ref
-параметр немного напоминает инициализированную локальную переменную, а out
-параметр — неинициализированную.)Иллюстрация:
private void func1(out string value)
{
Console.WriteLine(value); // нельзя, value не инициализировано
if (false)
return; // нельзя, забыли установить значение value
value = "Hello World!";
}
string s1;
func1(out s1);
private void func2(ref string value)
{
Console.WriteLine(value); // можно
if (false)
return; // не проблема, у value остаётся старое значение
value = "Hello World!";
}
string s2;
func2(ref s2); // нельзя, функция имеет право использовать значение,
// значит, оно должно быть инициализировано сначала
Таким образом,
out
-параметр — это как бы дополнительное возвращаемое значение функции. А ref
-параметр — просто параметр, изменения которого видны снаружи функции.@csharp_ci
Please open Telegram to view this post
VIEW IN TELEGRAM
File
:[HttpGet("file/{guid}/download")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<IActionResult> DownloadFile([FromRoute] Guid guid)
{
var file = await _mediaContentService.DownloadFile(guid);
return File(file.Stream, file.ContentType, file.FileName);
}
Можно ли каким-либо образом реализовать ограничение на скачивание файла, чтобы, например, в единицу времени не больше N пользователей качало файл?
File
— он возвращает ленивый ответ. Т.е. он не читает весь поток сразу, а ждет пока будет вызван метод от IActionResult
.Чтобы эту проблему решить, надо знать, когда файл точно отправлен.
Можно сделать специальный декоратор. Например:
[ApiController]
[Route("[controller]")]
public class SampleController : ControllerBase
{
private readonly IRateLimiter _rateLimiter;
public SampleController(IRateLimiter rateLimiter)
{
_rateLimiter = rateLimiter;
}
[HttpGet("connection")]
public async Task<IActionResult> DownLoadFile(Guid file)
{
var stream = await GetFileStream(file);
return new RateLimiterFileActionResult(File(stream, "content/type", "sample.txt"), _rateLimiter);
}
}
class RateLimiterFileActionResult : IActionResult
{
private readonly IActionResult _actionResultImplementation;
private readonly IRateLimiter _rateLimiter;
public RateLimiterFileActionResult(IActionResult actionResultImplementation, IRateLimiter rateLimiter)
{
_actionResultImplementation = actionResultImplementation;
_rateLimiter = rateLimiter;
}
public async Task ExecuteResultAsync(ActionContext context)
{
try
{
await _rateLimiter.ObtainAsync(context.HttpContext.RequestAborted);
await _actionResultImplementation.ExecuteResultAsync(context);
}
finally
{
await _rateLimiter.ReleaseAsync(context.HttpContext.RequestAborted);
}
}
}
public interface IRateLimiter
{
public Task ObtainAsync(CancellationToken token);
public Task ReleaseAsync(CancellationToken token);
}
Стоит заметить, что блокировка берётся внутри метода декоратора, а не в методе контроллера.
@csharp_ci
Please open Telegram to view this post
VIEW IN TELEGRAM
В этой статье мы разберёмся с запуском C#-тестов на Linux и последующую публикацию их в открытом репозитории кода на GitHub.
План:
├╼
Устанавливаем .NET на Ubuntu 22.04├╼
Создаём проект├╼
Запускаем сборку в GitHub Actions├╼
Используем секреты в коде тестов╰╼
Размещаем секреты в GitHub Actions Secrets@csharp_ci
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Что выведет этот код?
Anonymous Poll
25%
True
32%
False
34%
Возникнет ошибка компиляции
9%
Возникнет ошибка времени выполнения
Повторение — мать учения и основа научного метода, так что приступим)
Task.Yield()
возвращает специальное значение, предназначенное для передачи оператору await
, и в отрыве от этого оператора не имеющее смысла.Конструкция же
await Task.Yield()
делает довольно простую вещь — прерывает текущий метод и сразу же планирует его продолжение в текущем контексте синхронизации.Используется же эта конструкция для разных целей.
protected override async void OnClosing(CancelEventArgs e)
{
e.Cancel = true;
await Task.Yield();
// (какая-то логика)
}
using (var ts = new TransactionScope()) {
// ...
Foo();
// ...
ts.Complete();
}
async void Foo() {
// ... тут мы находимся в контексте транзакции
if (Transaction.Current != null) await Task.Yield();
// ... а тут его уже нет!
}
Например, рассмотрим упрощенную реализацию
AsyncLock
:class AsyncLock
{
private Task unlockedTask = Task.CompletedTask;
public async Task<Action> Lock()
{
var tcs = new TaskCompletionSource<object>();
await Interlocked.Exchange(ref unlockedTask, tcs.Task);
return () => tcs.SetResult(null);
}
}
Здесь поступающие запросы на получение блокировки выстраиваются в неявную очередь на продолжениях. Казалось бы, что может пойти не так?
private static async Task Foo()
{
var _lock = new AsyncLock();
var unlock = await _lock.Lock();
for (var i = 0; i < 100000; i++) Bar(_lock);
unlock();
}
private static async void Bar(AsyncLock _lock)
{
var unlock = await _lock.Lock();
// do something sync
unlock();
}
Здесь продолжение метода
Bar
вызывается в тот момент, когда другой метод Bar
выполняет вызов unlock()
. Получается косвенная рекурсия между методом Bar
и делегатом unlock
, которая быстро сжирает стек и ведет к его переполнению.Добавление же вызова
Task.Yield()
перенесет исполнение в "чистый" фрейм стека, и ошибка исчезнет:class AsyncLock
{
private Task unlockedTask = Task.CompletedTask;
public async Task<Action> Lock()
{
var tcs = new TaskCompletionSource<object>();
var prevTask = Interlocked.Exchange(ref unlockedTask, tcs.Task);
if (!prevTask.IsCompleted)
{
await prevTask;
await Task.Yield();
}
return () => tcs.SetResult(null);
}
}
@csharp_ci
Please open Telegram to view this post
VIEW IN TELEGRAM