en
Feedback
C#Hive: Projects & Progress | Программирование

C#Hive: Projects & Progress | Программирование

Open in Telegram

Сообщество единомышленников C#: решаем задачи, учимся, развиваемся и общаемся вместе. Советы по работе на фрилансе, готовые проекты, код ревью, рекомендации и исследования. Вопросы/сотрудничество: @tel_phil9

Show more
1 780
Subscribers
No data24 hours
-17 days
-1030 days
Posts Archive
Из примера выше, что будет выведено на экран?
Anonymous voting

*Если бы контрацептивы были человеком* #Юмор

Решение задачи к посту.
void CombSort(int[] values)
{
    double factor = 1.247;
    double step = values.Length - 1;

    while (step >= 1)
    {
        for (int i = 0; i + step < values.Length; i++)
        {
            ref int v1 = ref values[i];
            ref int v2 = ref values[(int)(i + step)];
            if (v1 > v2) Swap(ref v1, ref v2);
        }

        step /= factor;
    }
}

void Swap(ref int first, ref int second)
{
    int temp = first;
    first = second;
    second = temp;
}
#Задача #Решение #Полезно

🖥 Задача: сортировка расчёской (⭐️) Написать метод, который принимает массив целочисленного типа и реализует алгоритм сортировки расчёской данного массива. Ниже приведены примеры.
int[] arr = { 35, 1, 73, 8998, 534, -124, 89, 572, 9, 61 };
CombSort(arr);
👉 [-124, 1, 9, 35, 61, 73, 89, 534, 572, 8998]
int[] arr = { 100, 90, 80, 70, 60, 50, 40, 30, 20, 1 };
CombSort(arr);
👉 [1, 20, 30, 40, 50, 60, 70, 80, 90, 100]
int[] arr = { 4025, 740, 8628, 8790, 18 };
CombSort(arr);
👉 [18, 740, 4025, 8628, 8790]
int[] arr = { -101, 101, -4, -88, 909, -1007 };
CombSort(arr);
👉 [-1007, -101, -88, -4, 101, 909] Пишите варианты в комментариях. Решение будет сегодня вечером новым постом в канале. #Задача #Lvl1

🖥 Конструкция try..catch..finally Глядя на результаты последнего опроса, становится ясно, что не каждый до конца понимает работу конструкции try..catch..finally. Ввиду этого ниже мы разберём детальнее эту особенность языка C#. Иногда при выполнении программы возникают ошибки, которые трудно предусмотреть или предвидеть, а иногда вовсе невозможно. Например, при передачи файла по сети может неожиданно оборваться сетевое подключение. такие ситуации называются исключениями. Для обработки таких ситуаций предназначена конструкция try..catch..finally:
try
{
     
}
catch
{
     
}
finally
{
     
}
При использовании данной конструкции вначале выполняются все инструкции в блоке try. Если в этом блоке не возникло исключений, то после его выполнения начинает выполняться блок finally. И затем конструкция try..catch..finally завершает свою работу. Если же в блоке try вдруг возникает исключение, то обычный порядок выполнения останавливается, и среда CLR начинает искать блок catch, который может обработать данное исключение. Если нужный блок catch найден, то он выполняется, и после его завершения выполняется блок finally. ➡️ Разбор на примере
int x = 5;
int y = x / 0;
Console.WriteLine($"Результат: {y}");
Console.WriteLine("Конец программы");
Здесь происходит деление числа на 0, что приведёт к генерации исключения. И при запуске приложения в режиме отладки мы увидим в Visual Studio окошко, которое информирует об исключении. Единственное, что нам останется — это завершить выполнение программы. Чтобы избежать подобного аварийного завершения программы, следует использовать для обработки исключений конструкцию try..catch..finally. Так, перепишем пример следующим образом:
try
{
    int x = 5;
    int y = x / 0;
    Console.WriteLine($"Результат: {y}");
}
catch
{
    Console.WriteLine("Возникло исключение!");
}
finally
{
    Console.WriteLine("Блок finally");
}
Console.WriteLine("Конец программы");
Здесь также возникнет исключение в блоке try, по той же причине. И дойдя до строки деления на ноль выполнение программы остановится. CLR найдёт блок catch и передаст управление этому блоку. После блока catch будет выполняться блок finally:
Возникло исключение!
Блок finally
Конец программы
Таким образом, программа как и прежде не будет выполнять деление на ноль и, соответственно, не будет выводить результат этого деления, но теперь она не будет аварийно завершаться, а исключение будет обрабатываться в блоке catch. Следует отметить, что в этой конструкции обязателен блок try. При наличии блока catch мы можем опустить блок finally. И, наоборот, при наличии блока finally мы можем опустить блок catch и не обрабатывать исключение. ➡️ Блок finally Именно из-за этого блока бОльшая часть ответов в последнем опросе является ошибочным. Особенности блока: Выполняется в любом случае (при успешном выполнении try; при выбрасывании исключения; при передачи управления по break или return); Блок необязателен; Если есть finally, блоки catch необязательны; Выполняется последним, после блока try и всех выполняемых блоков catch. Если говорить кратко, то данный блок выполняется всегда перед выходом из всей конструкции try..catch..finally. #Полезно #Exception #TryCatch

Из примера выше, что будет выведено на экран?
Anonymous voting

🖥 Await внутри конструкции lock Сегодня рассмотрим вопрос с собеседования, где предоставлен следующий код:
object sync = new();

lock (sync)
{
    await MethodAsync();
}

async Task MethodAsync()
{
    // our code
    // ...
}
Что будет с этим кодом и почему? Под спойлером ниже подробно разобран ответ, поэтому не спеши его смотреть, если интересно подумать и ответить самостоятельно. Сперва отмечу, что данный код не скомпилируется. Можно подумать, что для команды разработчиков компилятора это слишком сложно/невозможно реализовать, но нет. Тот факт, что энтузиасты из stackoverflow пытались обойти это ограничение — прямое тому подтверждение. Скорее это невероятно плохая идея, потому это не допускается, чтобы избежать ошибок. Ожидание внутри блокировки — это рецепт создания взаимоблокировок (deadlocks). ➡️ Инверсия блокировок Между моментом, когда await возвращает управление вызывающему потоку и моментом, когда метод возобновляет работу — выполняется произвольный код. Этот произвольный код может снимать блокировки, которые приводят к инверсии порядка блокировок и, следовательно, к взаимоблокировкам. ➡️ Выполнение в разных потоках Более того, обычно вы снова возобновляете работу в потоке, который выполнил await, но это не обязательно. В сложных сценариях код может возобновиться в другом потоке, что не приведёт к разблокировке объекта sync исходным потоком. #Полезно #Async #Await #Собеседование

Решение задачи к посту.
void ShakerSort(int[] values)
{
    int left = 0;
    int right = values.Length - 1;
    bool needContinue;

    do
    {
        needContinue = false;

        for (int i = left; i < right; i++)
        {
            if (values[i] > values[i + 1])
            {
                Swap(ref values[i], ref values[i + 1]);
                needContinue = true;
            }
        }
        right--;

        for (int i = right; i > left; i--)
        {
            if (values[i] < values[i - 1])
            {
                Swap(ref values[i], ref values[i - 1]);
                needContinue = true;
            }
        }
        left++;
    } while (needContinue);
}

void Swap(ref int first, ref int second)
{
    int temp = first;
    first = second;
    second = temp;
}
#Задача #Решение #Полезно

🖥 Задача: шейкерная сортировка (⭐️) Написать метод, который принимает массив целочисленного типа и реализует алгоритм шейкерной сортировки данного массива. Ниже приведены примеры.
int[] arr = { 94, 1, 0, -100, 88, -3, 18 };
ShakerSort(arr);
👉 [-100, -3, 0, 1, 18, 88, 94]
int[] arr = { 5, 8, 7, 1, 14, 2, 13, 9, 11, 6, 10, 15, 12, 3, 4 };
ShakerSort(arr);
👉 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
int[] arr = { 2, 3, 4, 1 };
ShakerSort(arr);
👉 [1, 2, 3, 4]
int[] arr = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
ShakerSort(arr);
👉 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] Пишите варианты в комментариях. Решение будет сегодня вечером новым постом в канале. #Задача #Lvl1

Из примера выше, в каком месте возникнет ошибка?
Anonymous voting

🖥 Span и многомерные массивы Мы уже знаем, что из себя представляет структура Span, позволяющая эффективно работать с памятью. Однако явным образом структура принимает только одномерные массивы. Если мы попробуем написать следующее для двухмерного массива, то результат, который мы ожидаем, получен не будет:
int[,] array = { };
Span<int> span1 = array; // Ошибка компиляции
Span<int> span2 = array.AsSpan(); // Ошибка компиляции
Однако один из перегруженных конструкторов структуры Span имеет возможность указания по указателю, благодаря чему мы можем написать следующий метод:
unsafe Span<int> AsSpan(int[,] matrix)
{
    fixed (int* p = matrix) return new Span<int>(p, matrix.Length);
}
Таким подходом двухмерный массив, как и любой другой многомерный массив, сможет теперь управляться Span`ом. Используем его:
int[,] arr = { { 1, 2 }, { 3, 4 } };
Console.WriteLine($"{arr[0, 0]} {arr[0, 1]} {arr[1, 0]} {arr[1, 1]}");

Span<int> span = AsSpan(arr);
span[0] = 0;
span[3] = 0;
Console.WriteLine($"{arr[0, 0]} {arr[0, 1]} {arr[1, 0]} {arr[1, 1]}");
Вывод:
1 2 3 4
0 2 3 0
Минус такого подхода в том, что Span работает с непрерывной областью памяти, а это может быть не очень удобно при попытке выделить определённые элементы, ведь они в Span будут предоставлены последовательно:
int[,] arr = { { 55, 12 }, { 97, 0 } };
Span<int> span = AsSpan(arr); // [55, 12, 97, 0]
Мы можем выделить промежуток элементов с 1 по 2 или с 2 по 4, но выделить только 1 и 4 элемент, без выделения дополнительной памяти, у нас не получится. Тем не менее, Span этим и прекрасен. ➡️ Выделение строки из матрицы А чтобы из матрицы (двухмерного массива) выделить все элементы N-ой строки, мы можем написать нечто следующее:
unsafe Span<int> AsRowSpan(int[,] matrix, int indexRow)
{
    fixed (int* p = matrix)
    {
        var span = new Span<int>(p, matrix.Length);
        int columns = matrix.GetLength(1);
        int start = indexRow * columns;

        return span.Slice(start, columns);
    }
}
Применим:
int[,] array2D = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
Span<int> span = AsRowSpan(array2D, 1); // [4, 5, 6]

span.Fill(-1);
// Теперь array2D = [1, 2, 3, -1, -1, -1, 7, 8, 9]
Либо же использовать существующее решение, приводящее к аналогичному результату:
int[,] array2D = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
Span<int> span = MemoryMarshal.CreateSpan(ref array2D[1, 0], array2D.GetLength(1));
#Полезно #Span #Array #Unsafe #Pointers

Решение задачи к посту.
void BubbleSort<T>(T[] arr, bool byDescending) where T : struct, IComparable<T>
{
    int end = arr.Length;
    int targetSort = byDescending ? -1 : 1;

    while (end != 0)
    {
        end--;

        for (int i = 0; i < end; i++)
        {
            if (arr[i].CompareTo(arr[i + 1]) == targetSort)
            {
                T temp = arr[i];
                arr[i] = arr[i + 1];
                arr[i + 1] = temp;
            }
        }
    }
}
#Задача #Решение #Полезно

🖥 Задача: пузырьковая сортировка (⭐️) Написать обобщённый метод, который принимает массив любого значимого типа и булевое значение, отвечающее за сортировку по убыванию/возрастанию. Метод должен реализовать алгоритм пузырьковой сортировки исходного массива. Ниже приведены примеры.
int[] array = { 4, 1, 2, 6, 3, 5, 2 };
BubbleSort<int>(arr: array, byDescending: false);
👉 [1, 2, 2, 3, 4, 5, 6]
long[] array = { 55, 11, 41763609734, 63, 8, -336, 128, 95, 0 };
BubbleSort(array, true);
👉 [41763609734, 128, 95, 63, 55, 11, 8, 0, -336]
float[] array = { -1.23f, 3.14f, 9 };
BubbleSort(array, true);
👉 [9, 3,14, -1,23]
DateTime[] array = { new DateTime(2012, 12, 21), new DateTime(2024, 11, 7), DateTime.Parse("14.8.2021") };
BubbleSort(array, false);
👉 [21.12.2012 00:00:00, 14.08.2021 00:00:00, 07.11.2024 00:00:00] Пишите варианты в комментариях. Решение будет сегодня вечером новым постом в канале. #Задача #Lvl1

🖥 Основные типы делегатов и замыкания В .NET есть несколько встроенных делегатов, которые используются в различных ситуациях
🖥 Основные типы делегатов и замыкания В .NET есть несколько встроенных делегатов, которые используются в различных ситуациях. И наиболее используемыми являются Action, Predicate и Func. ➡️ Action Делегат представляет собой некоторое действие, которое ничего не возвращает, то есть в качестве возвращаемого типа имеет тип void:
public delegate void Action()
public delegate void Action<in T>(T obj)
Обобщённый делегат имеет ряд перегруженных версий. Каждая версия принимает разное число параметров: от Action<in T> до Action<in T1, in T2, ... in T16>. Таким образом можно передать до 16 значений в метод. Как правило, этот делегат передаётся в качестве параметра метода и предусматривает вызов определённых действий в ответ на произошедшее действие:
DoOperation(10, 6, Add); // 10 + 6 = 16
DoOperation(10, 6, Multiply); // 10 * 6 = 60

void DoOperation(int a, int b, Action<int, int> op) => op(a, b);
void Add(int x, int y) => Console.WriteLine($"{x} + {y} = {x + y}");
void Multiply(int x, int y) => Console.WriteLine($"{x} * {y} = {x * y}");
➡️ Predicate Делегат представляет собой некоторое условие, который принимает один параметр и возвращает значение типа bool:
delegate bool Predicate<in T>(T obj);
Как правило, используется для сравнения, сопоставления некоторого объекта T определённому условию. В качестве выходного результата возвращается значение true, если условие соблюдено, и false, если не соблюдено:
Predicate<int> isPositive = (int x) => x > 0;
 
Console.WriteLine(isPositive(20));
Console.WriteLine(isPositive(-20));
➡️ Func Делегат представляет собой функцию, которая возвращает результат действия и может принимать параметры:
public delegate TResult Func<out TResult>()
public delegate TResult Func<in T, out TResult>(T arg)
Он также имеет различные формы: от Func<out T> до Func<in T1, in T2, ... in T16, out TResult>, т.е. может принимать до 16 параметров. Данный делегат также часто используется в качестве параметра в методах:
int result = DoOperation(6, DoubleNumber);
Console.WriteLine(result); // 12

result = DoOperation(6, SquareNumber);
Console.WriteLine(result); // 36

int DoOperation(int n, Func<int, int> operation) => operation(n);
int DoubleNumber(int n) => 2 * n;
int SquareNumber(int n) => n * n;
➡️ Замыкания Замыкание — это объект функции, который запоминает своё лексическое окружение даже в том случае, когда она выполняется вне своей области видимости. Технически, замыкание включает: ⏺Внешняя функция, которая определяет некоторую область видимости и в которой определены некоторые переменные и параметры;Лексическое окружение — переменные и параметры, которые определены во внешней функции;Вложенная функция, которая использует переменные и параметры внешней функции. Рассмотрим реализацию замыканий через локальные функции:
var fn = Outer(); // Возвращает метод Inner()

// Вызываем внутреннюю функцию Inner()
fn(); // 6
fn(); // 7
fn(); // 8

Action Outer() // Метод или внешняя функция
{
    int x = 5; // Лексическое окружение — локальная переменная
    return Inner; // Возвращаем локальную функцию

    void Inner() // Локальная функция
    {
        x++; // Операции с лексическим окружением
        Console.WriteLine(x);
    }
}
Переменная fn и представляет собой замыкание, т.к. объединяет функцию и окружение, в котором функция была создана. И несмотря на то, что мы получили локальную функцию и можем её вызывать вне её метода, в котором она определена, она запомнила своё лексическое окружение и может к нему обращаться и изменять. ➡️ Применение параметров в замыкании Кроме внешних переменных к лексическому окружению также относятся параметры окружающего метода:
var fn = Multiply(5);

Console.WriteLine(fn(5)); // 25
Console.WriteLine(fn(6)); // 30
Console.WriteLine(fn(7)); // 35

Func<int, int> Multiply(int n)
{
    return Inner;

    int Inner(int m) => n * m;
}
Как итог, при вызове метода Multiply определяется переменная fn, которая получает локальную функцию Inner и её лексическое окружение — значение параметра n (в нашем случае n = 5). #Полезно #Delegate

Из примера выше, что будет выведено на экран?
Anonymous voting