Anonymous Quiz
78%
public async Task<int> GetData() { return await Task.FromResult(42); }
3%
public async int GetData() { return 42; }
13%
public Task<int> GetData() { return Task.FromResult(42); }
5%
public async Task GetData() { return 42; }
🤯7
Для оптимизации LINQ запросов в C# используйте компилируемые запросы, минимизируйте запросы в циклах, выбирайте только нужные поля, применяйте отложенное выполнение и метод
.AsNoTracking() для чтения данных.LINQ-запросы не выполняются до тех пор, пока не начнётся итерация по ним. Это позволяет объединить несколько операций в один запрос к базе данных.
var query = dbContext.Users.Where(u => u.IsActive); // Запрос ещё не выполнен
var activeUsers = query.ToList(); // Запрос выполнен здесь
Избегайте ненужных вызовов
ToList и ToArray, так как они приводят к немедленному выполнению запроса и загрузке всех данных в память.var users = dbContext.Users.Where(u => u.IsActive).ToList(); // Не вызывайте ToList() до тех пор, пока не нужно
Выбирайте только необходимые поля, чтобы уменьшить объем данных, передаваемых по сети и загружаемых в память.
var userNames = dbContext.Users
.Where(u => u.IsActive)
.Select(u => new { u.FirstName, u.LastName })
.ToList();
Фильтруйте данные на уровне базы данных, а не в памяти, чтобы уменьшить количество передаваемых данных.
var activeUsers = dbContext.Users.Where(u => u.IsActive).ToList(); // Фильтрация на уровне базы данных
Используйте пагинацию для работы с большими наборами данных, чтобы загружать и обрабатывать только нужные страницы.
var pageNumber = 2;
var pageSize = 10;
var paginatedUsers = dbContext.Users
.Where(u => u.IsActive)
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize)
.ToList();
Сложные LINQ-запросы в циклах могут привести к многочисленным запросам к базе данных. Постарайтесь минимизировать их или объединить в один запрос.
// Плохо
foreach (var userId in userIds)
{
var user = dbContext.Users.FirstOrDefault(u => u.Id == userId);
}
// Хорошо
var users = dbContext.Users.Where(u => userIds.Contains(u.Id)).ToList();
Асинхронные методы, такие как
ToListAsync, FirstOrDefaultAsync и SingleOrDefaultAsync, помогают избежать блокировки потоков и улучшить производительность.var activeUsers = await dbContext.Users.Where(u => u.IsActive).ToListAsync();
Для часто выполняемых запросов используйте
EF.CompileQuery в Entity Framework, чтобы уменьшить накладные расходы на компиляцию запросов.private static readonly Func<MyDbContext, bool, IQueryable<User>> GetActiveUsersQuery =
EF.CompileQuery((MyDbContext context, bool isActive) =>
context.Users.Where(u => u.IsActive == isActive));
var activeUsers = GetActiveUsersQuery(dbContext, true).ToList();
Используйте инструменты профилирования и логирования для анализа генерируемых SQL-запросов, чтобы найти потенциальные узкие места и неэффективные запросы.
var sql = dbContext.Users.Where(u => u.IsActive).ToQueryString();
Console.WriteLine(sql);
Убедитесь, что индексы в базе данных настроены правильно для часто выполняемых запросов, чтобы улучшить производительность поиска.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍25
Anonymous Quiz
59%
var tuple = new Tuple<int, string>(1, "one");
9%
var tuple = Tuple.Create(1, "one");
12%
var tuple = new Tuple(1, "one");
20%
var tuple = (1, "one");
👍6👀5🤔3😁2
Обработка исключений в C# необходима для правильного управления ошибками, возникающими во время выполнения программы. Она позволяет перехватывать ошибки и выполнять соответствующие действия, чтобы программа не завершалась аварийно. Рассмотрим основные конструкции для обработки исключений:
try, catch, finally, и throw.Конструкция
try-catch используется для перехвата и обработки исключений. Код, который может вызвать исключение, помещается в блок try, а код для обработки исключения - в блок catch. try
{
// Код, который может вызвать исключение
int result = 10 / 0;
}
catch (DivideByZeroException ex)
{
// Обработка исключения
Console.WriteLine("Произошло деление на ноль: " + ex.Message);
}
catch (Exception ex)
{
// Обработка других исключений
Console.WriteLine("Произошла ошибка: " + ex.Message);
}
Блок
finally выполняется в любом случае после завершения блоков try и catch, независимо от того, возникло исключение или нет. Он часто используется для освобождения ресурсов, таких как файловые дескрипторы или сетевые подключения. try
{
// Код, который может вызвать исключение
using (StreamReader sr = new StreamReader("file.txt"))
{
// Чтение файла
}
}
catch (FileNotFoundException ex)
{
// Обработка исключения
Console.WriteLine("Файл не найден: " + ex.Message);
}
finally
{
// Очистка ресурсов
Console.WriteLine("Закрытие файла.");
}
Ключевое слово
throw используется для генерации исключений. Оно может быть использовано как для передачи возникшего исключения дальше по цепочке вызовов, так и для создания нового исключения. void ProcessNumber(int number)
{
if (number < 0)
{
throw new ArgumentOutOfRangeException("number", "Число должно быть неотрицательным");
}
// Продолжение обработки
}
Вложенные исключения позволяют захватывать одно исключение и генерировать новое, сохраняя информацию о первоначальном исключении.
try
{
// Код, который может вызвать исключение
MethodThatThrows();
}
catch (Exception ex)
{
throw new InvalidOperationException("Ошибка в процессе выполнения", ex);
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8
Anonymous Quiz
12%
var result = from x in list where x > 10;
5%
var result = list.Find(x => x > 10);
8%
var result = list.Filter(x => x > 10);
75%
var result = list.Where(x => x > 10);
🤯7
Использование специализированных исключений, таких как
ArgumentException, InvalidOperationException, или пользовательских исключений (например, MyCustomException), лучше, чем использование общего исключения Exception. Это позволяет более точно и детально управлять ошибками.Специализированные исключения помогают точнее определить, что именно пошло не так. Например,
ArgumentNullException ясно указывает, что переданный аргумент был null, в то время как Exception может означать любую ошибку. void ProcessData(string data)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data), "Data cannot be null");
}
// Обработка данных
}
Использование конкретных исключений делает код более читаемым и поддерживаемым. Разработчики, читающие код, сразу понимают, какие именно ошибки могут возникнуть и как с ними справляться.
try
{
// Код, который может вызвать исключение
}
catch (ArgumentNullException ex)
{
// Обработка ситуации, когда аргумент был null
}
catch (InvalidOperationException ex)
{
// Обработка некорректного состояния операции
}
Специализированные исключения предоставляют больше информации для отладки. Это позволяет быстрее найти и исправить ошибки, так как разработчики получают более точные сообщения об ошибках.
try
{
// Код, который может вызвать исключение
}
catch (Exception ex)
{
// Общее исключение, трудно определить причину
Console.WriteLine("Произошла ошибка: " + ex.Message);
}
С помощью специализированных исключений можно различать разные типы ошибок и применять к ним разные стратегии обработки. Это делает обработку ошибок более гибкой и точной.
try
{
// Код, который может вызвать исключение
}
catch (ArgumentNullException ex)
{
Console.WriteLine("Обязательный аргумент не был передан: " + ex.Message);
// Специфическая обработка для отсутствующего аргумента
}
catch (ArgumentException ex)
{
Console.WriteLine("Аргумент некорректен: " + ex.Message);
// Специфическая обработка для некорректного аргумента
}
В сложных приложениях можно создавать пользовательские исключения, которые более точно описывают уникальные ошибки, специфичные для вашего приложения.
public class MyCustomException : Exception
{
public MyCustomException(string message) : base(message) { }
}
void DoSomething()
{
throw new MyCustomException("Произошла специфическая ошибка.");
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🤔1🤯1👾1
Anonymous Quiz
68%
public readonly int x = 10;
2%
public int x = 10;
12%
public static readonly int x = 10;
19%
readonly int x;
🤯21😁4
Создание своих исключений в C# может понадобиться в ряде случаев, когда стандартные исключения не подходят для описания специфических ошибок, возникающих в вашем приложении.
Свои исключения позволяют дать более точное описание ошибки, связанной с конкретной бизнес-логикой или контекстом приложения. Это делает код более понятным и легким для сопровождения.
public class InsufficientFundsException : Exception
{
public InsufficientFundsException(string message) : base(message) { }
}
public class BankAccount
{
public void Withdraw(decimal amount)
{
if (amount > Balance)
{
throw new InsufficientFundsException("Недостаточно средств на счете.");
}
// Логика снятия средств
}
}
Свои исключения могут включать дополнительную информацию, которая поможет лучше понять контекст ошибки при отладке или логировании.
public class DataNotFoundException : Exception
{
public string DataId { get; }
public DataNotFoundException(string dataId, string message) : base(message)
{
DataId = dataId;
}
}
void GetData(string id)
{
// Если данные не найдены
throw new DataNotFoundException(id, "Данные не найдены для ID: " + id);
}
Создание своих исключений позволяет логически группировать ошибки, что облегчает их обработку в коде. Например, можно создать базовое исключение для всех ошибок, связанных с доступом к базе данных, и наследовать от него конкретные типы ошибок.
public class DatabaseException : Exception
{
public DatabaseException(string message) : base(message) { }
}
public class RecordNotFoundException : DatabaseException
{
public RecordNotFoundException(string message) : base(message) { }
}
public class ConnectionFailedException : DatabaseException
{
public ConnectionFailedException(string message) : base(message) { }
}
В сложных системах с многоуровневой архитектурой свои исключения помогают управлять ошибками на каждом уровне, передавая их на более высокий уровень для централизованной обработки.
public class BusinessException : Exception
{
public BusinessException(string message) : base(message) { }
}
public class PaymentProcessingException : BusinessException
{
public PaymentProcessingException(string message) : base(message) { }
}
void ProcessPayment()
{
try
{
// Логика обработки платежа
}
catch (Exception ex)
{
throw new PaymentProcessingException("Ошибка при обработке платежа: " + ex.Message);
}
}
Пользовательские исключения могут содержать информацию, которая будет полезна для отображения пользователю, улучшая пользовательский опыт.
public class UserNotAuthorizedException : Exception
{
public UserNotAuthorizedException(string message) : base(message) { }
}
void AccessRestrictedArea()
{
if (!userIsAuthorized)
{
throw new UserNotAuthorizedException("Пользователь не имеет права доступа к этому разделу.");
}
// Логика доступа
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9😁1
Anonymous Quiz
3%
Типы данных, которые нельзя изменять.
95%
Типы, которые могут принимать значение null.
1%
Типы, используемые для сериализации.
1%
Типы, используемые для работы с потоками.
😁14🤯5
Валидация входных данных помогает предотвратить атаки, такие как SQL-инъекции, XSS (межсайтовый скриптинг) и другие. SQL-инъекции: Используйте параметризованные запросы или ORM (например, Entity Framework).
using (SqlCommand cmd = new SqlCommand("SELECT * FROM Users WHERE Username = @username", conn))
{
cmd.Parameters.AddWithValue("@username", username);
// Выполнение команды
}XSS: Используйте библиотеку для экранирования HTML, например, AntiXSS.
string safeContent = Microsoft.Security.Application.Encoder.HtmlEncode(userInput);
Обеспечьте надежную аутентификацию и разграничение доступа к ресурсам.
Аутентификация: Используйте современные методы аутентификации, такие как OAuth, OpenID Connect.
Авторизация: Применяйте ролевую или заявочную (claims-based) авторизацию.
[Authorize(Roles = "Admin")]
public IActionResult AdminOnly()
{
return View();
}
Используйте анти-CSRF токены для защиты от CSRF атак.
<form asp-action="Create">
<input type="hidden" name="__RequestVerificationToken" value="@Antiforgery.GetTokens(HttpContext).RequestToken" />
<!-- Другие поля формы -->
</form>
Шифруйте чувствительные данные как при передаче, так и при хранении.
При передаче: Используйте HTTPS для шифрования данных, передаваемых через сеть.
При хранении: Используйте библиотеки для шифрования, такие как
System.Security.Cryptography. using (Aes aes = Aes.Create())
{
aes.Key = key;
aes.IV = iv;
// Шифрование данных
}
Внедрите логирование и мониторинг для обнаружения и анализа подозрительной активности.
Логирование: Логируйте важные действия, такие как входы в систему, изменения данных.
Мониторинг: Используйте инструменты мониторинга, такие как Application Insights, для отслеживания состояния приложения.
_logger.LogInformation("User {UserId} logged in.", userId);Не показывайте подробные сообщения об ошибках пользователям, чтобы не раскрывать внутреннюю структуру приложения.
Обработка исключений: Ловите и корректно обрабатывайте исключения, предоставляя пользователю дружелюбные сообщения.
try
{
// Код, который может вызвать исключение
}
catch (Exception ex)
{
_logger.LogError(ex, "Произошла ошибка.");
return View("Error");
}
Регулярно обновляйте используемые библиотеки и фреймворки, чтобы закрывать уязвимости.
Удалите или отключите ненужные функции и сервисы, чтобы минимизировать возможные точки входа для атак.
Защитите конфигурационные файлы, содержащие чувствительную информацию.
Секреты и ключи: Используйте секреты и безопасное хранилище для конфиденциальной информации.
var connectionString = Configuration["ConnectionStrings:DefaultConnection"];
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7
Anonymous Quiz
5%
ToString.
3%
Parse.
3%
Convert.
89%
Serialize.
🤯12🤔3👍1👀1
Работа с асинхронными операциями в C# важна для создания производительных и отзывчивых приложений, особенно когда дело касается операций ввода-вывода, сетевых запросов или длительных вычислений. В C# для этого используется ключевые слова
async и await, а также Task и Task<T>. `async`: Указывает, что метод является асинхронным и может содержать операторы
await.`await`: Используется для ожидания завершения асинхронной операции без блокировки основного потока.
`Task`: Представляет асинхронную операцию без возвращаемого значения.
`Task<T>`: Представляет асинхронную операцию, возвращающую значение типа
T.public async Task DelayExampleAsync()
{
Console.WriteLine("Начало задержки");
await Task.Delay(2000); // Асинхронная задержка на 2 секунды
Console.WriteLine("Завершение задержки");
}
Асинхронные операции ввода-вывода часто используются для работы с файлами, сетевыми запросами и базами данных. Пример асинхронного чтения файла:
public async Task<string> ReadFileAsync(string filePath)
{
using (StreamReader reader = new StreamReader(filePath))
{
return await reader.ReadToEndAsync();
}
}
Для выполнения асинхронных HTTP-запросов можно использовать
HttpClient. public async Task<string> FetchDataFromApiAsync(string url)
{
using (HttpClient client = new HttpClient())
{
HttpResponseMessage response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
Обработка исключений в асинхронных методах аналогична синхронным методам, с использованием блоков
try-catch. public async Task<string> SafeFetchDataFromApiAsync(string url)
{
try
{
using (HttpClient client = new HttpClient())
{
HttpResponseMessage response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
catch (HttpRequestException ex)
{
Console.WriteLine($"Ошибка запроса: {ex.Message}");
return string.Empty;
}
}
Для выполнения нескольких асинхронных операций параллельно можно использовать
Task.WhenAll или Task.WhenAny. public async Task PerformMultipleTasksAsync()
{
Task<int> task1 = Task.Run(() => ComputeAsync(10));
Task<int> task2 = Task.Run(() => ComputeAsync(20));
int[] results = await Task.WhenAll(task1, task2);
Console.WriteLine($"Результаты: {results[0]}, {results[1]}");
}
public async Task<int> ComputeAsync(int value)
{
await Task.Delay(1000);
return value * value;
}
Рассмотрим пример асинхронного метода, который загружает данные из нескольких URL и выводит их размер.
public async Task FetchDataFromMultipleUrlsAsync(string[] urls)
{
List<Task<string>> fetchTasks = new List<Task<string>>();
foreach (string url in urls)
{
fetchTasks.Add(FetchDataFromApiAsync(url));
}
string[] results = await Task.WhenAll(fetchTasks);
foreach (string result in results)
{
Console.WriteLine($"Длина данных: {result.Length}");
}
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤1
Тестирование программного обеспечения включает планирование тестов, их выполнение для проверки функциональности и других аспектов, а отладка занимается устранением найденных в ходе тестирования ошибок через анализ кода и логов.
Visual Studio предоставляет мощные инструменты отладки. Основные возможности включают установку точек останова (breakpoints), пошаговое выполнение кода (step over, step into, step out), просмотр значений переменных и выражений.
Для вывода отладочной информации можно использовать классы
Debug и Trace из пространства имен System.Diagnostics. using System.Diagnostics;
public class Program
{
public static void Main()
{
Debug.WriteLine("This is a debug message.");
Trace.WriteLine("This is a trace message.");
}
}
Использование журналирования позволяет записывать события и ошибки во время выполнения приложения. В .NET Core и ASP.NET Core используется встроенная система логирования.
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index()
{
_logger.LogInformation("Executing Index method");
return View();
}
}
В случае критических ошибок можно анализировать дампы памяти для диагностики проблем. Используйте WinDbg или Visual Studio для анализа дампов.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11
Anonymous Quiz
14%
Управляет потоками.
5%
Создает новую задачу.
80%
Отслеживает завершение асинхронной операции.
1%
Сериализует данные.
❤7
Static - это ключевое слово, которое используется для объявления статических членов в классе. Статические члены принадлежат самому классу, а не его экземплярам.
Статические поля объявляются с использованием ключевого слова
static и принадлежат классу в целом. Все экземпляры класса разделяют одно и то же статическое поле.public class Example
{
public static int Counter;
}
// Доступ к статическому полю
Example.Counter = 10;
Console.WriteLine(Example.Counter); // Выведет 10
Статические методы могут быть вызваны без создания экземпляра класса. Статические методы могут обращаться только к другим статическим членам класса.
public class Calculator
{
public static int Add(int a, int b)
{
return a + b;
}
}
// Вызов статического метода
int sum = Calculator.Add(5, 3);
Console.WriteLine(sum); // Выведет 8
Статические свойства похожи на статические поля, но с доступом через методы get и set.
public class Configuration
{
private static string _setting;
public static string Setting
{
get { return _setting; }
set { _setting = value; }
}
}
// Доступ к статическому свойству
Configuration.Setting = "My Setting";
Console.WriteLine(Configuration.Setting); // Выведет "My Setting"
Статические конструкторы используются для инициализации статических членов класса. Они вызываются один раз при первом обращении к статическим членам.
public class Database
{
public static string ConnectionString;
static Database()
{
ConnectionString = "Initial Catalog=myDatabase;Data Source=myServer;";
}
}
// При первом доступе вызывается статический конструктор
Console.WriteLine(Database.ConnectionString); // Выведет строку подключения
Статические члены полезны для хранения данных или функций, которые должны быть общими для всех экземпляров класса.
Позволяет вызывать методы и свойства без необходимости создавать экземпляр класса.
Статические конструкторы полезны для инициализации ресурсов или настройки параметров, необходимых для работы класса.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12❤1
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10😁2👀1
Статический конструктор в предназначен для инициализации статических членов класса или для выполнения действий, которые должны быть выполнены один раз для всего класса, а не для каждого его экземпляра.
Статический конструктор вызывается автоматически перед первым использованием любого статического члена класса или перед созданием первого экземпляра класса, в зависимости от того, что произойдет раньше.
Статический конструктор выполняется только один раз на весь период существования приложения.
Статический конструктор не может принимать параметры.
Невозможно вызвать статический конструктор вручную. Он вызывается автоматически.
Рассмотрим класс, который использует статический конструктор для инициализации статического поля:
public class ConfigurationManager
{
public static string Configuration;
// Статический конструктор
static ConfigurationManager()
{
Configuration = "Default Configuration";
Console.WriteLine("Статический конструктор выполнен.");
}
}
class Program
{
static void Main()
{
// Обращение к статическому полю вызывает статический конструктор
Console.WriteLine(ConfigurationManager.Configuration);
// Создание экземпляра класса не вызовет статический конструктор снова
ConfigurationManager configManager = new ConfigurationManager();
Console.WriteLine(ConfigurationManager.Configuration);
}
}
Когда программа впервые обращается к
ConfigurationManager.Configuration, вызывается статический конструктор класса ConfigurationManager.Внутри статического конструктора происходит инициализация статического поля
Configuration.В консоли будет выведено сообщение "Статический конструктор выполнен." и значение "Default Configuration".
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6❤1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16🔥4
Статический полиморфизм, также известный как полиморфизм на этапе компиляции, — это форма полиморфизма, при которой вызов метода или доступ к члену определяется во время компиляции, а не во время выполнения. Статический полиморфизм достигается с помощью таких механизмов, как перегрузка методов (method overloading) и обобщенные типы (generics).
Перегрузка методов позволяет иметь несколько методов с одинаковым именем, но разными параметрами (различные типы, количество или порядок параметров). Компилятор решает, какой метод вызвать, основываясь на переданных аргументах.
public class MathOperations
{
public int Add(int a, int b)
{
return a + b;
}
public double Add(double a, double b)
{
return a + b;
}
public int Add(int a, int b, int c)
{
return a + b + c;
}
}
class Program
{
static void Main()
{
MathOperations math = new MathOperations();
int result1 = math.Add(2, 3); // Вызовется метод Add(int, int)
double result2 = math.Add(2.5, 3.5); // Вызовется метод Add(double, double)
int result3 = math.Add(1, 2, 3); // Вызовется метод Add(int, int, int)
Console.WriteLine(result1); // Вывод: 5
Console.WriteLine(result2); // Вывод: 6.0
Console.WriteLine(result3); // Вывод: 6
}
}
Обобщенные типы позволяют создавать классы, методы и структуры, работающие с любыми типами данных, обеспечивая безопасность типов на этапе компиляции и избегая затрат на приведение типов.
public class GenericCalculator<T>
{
public T Add(T a, T b)
{
dynamic da = a;
dynamic db = b;
return da + db;
}
}
class Program
{
static void Main()
{
GenericCalculator<int> intCalculator = new GenericCalculator<int>();
int intResult = intCalculator.Add(3, 4); // Вызов метода для типа int
Console.WriteLine(intResult); // Вывод: 7
GenericCalculator<double> doubleCalculator = new GenericCalculator<double>();
double doubleResult = doubleCalculator.Add(2.5, 3.7); // Вызов метода для типа double
Console.WriteLine(doubleResult); // Вывод: 6.2
}
}
В этом примере компилятор не знает заранее, какой метод
Speak будет вызван, так как это определяется во время выполнения.public class Animal
{
public virtual void Speak()
{
Console.WriteLine("Animal speaks");
}
}
public class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("Dog barks");
}
}
class Program
{
static void Main()
{
Animal myAnimal = new Dog();
myAnimal.Speak(); // Вывод: Dog barks
}
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🔥1