Паттерны проектирования для решения распространенных задач
Паттерны проектирования — это проверенные временем решения общих проблем в разработке программного обеспечения. Они помогают сделать код более гибким и поддерживаемым. Рассмотрим пример использования паттерна "Одиночка" (Singleton) и "Стратегия" (Strategy).
Singleton
Паттерн Singleton гарантирует, что у класса будет единственный экземпляр, и предоставляет глобальную точку доступа к нему.
Пример паттерна "Стратегия" (Strategy)
Паттерн Strategy позволяет определить семейство алгоритмов, инкапсулировать каждый из них и сделать их взаимозаменяемыми.
1. Создаем интерфейс стратегии и конкретные стратегии:
2. Создаем контекст, использующий стратегию:
3. Используем паттерн Strategy:
Преимущества использования паттернов проектирования:
1. Повышение повторного использования кода.
2. Улучшение читаемости и поддержки.
3. Гибкость и расширяемость.
Паттерны проектирования — это проверенные временем решения общих проблем в разработке программного обеспечения. Они помогают сделать код более гибким и поддерживаемым. Рассмотрим пример использования паттерна "Одиночка" (Singleton) и "Стратегия" (Strategy).
Singleton
Паттерн Singleton гарантирует, что у класса будет единственный экземпляр, и предоставляет глобальную точку доступа к нему.
public sealed class Singleton
{
private static readonly Lazy<Singleton> instance = new Lazy<Singleton>(() => new Singleton());
private Singleton()
{
// Приватный конструктор предотвращает создание экземпляров извне.
}
public static Singleton Instance
{
get
{
return instance.Value;
}
}
public void DoWork()
{
Console.WriteLine("Singleton работает.");
}
}
// Использование
class Program
{
static void Main()
{
Singleton.Instance.DoWork();
}
}
Пример паттерна "Стратегия" (Strategy)
Паттерн Strategy позволяет определить семейство алгоритмов, инкапсулировать каждый из них и сделать их взаимозаменяемыми.
1. Создаем интерфейс стратегии и конкретные стратегии:
public interface IStrategy
{
void Execute();
}
public class ConcreteStrategyA : IStrategy
{
public void Execute()
{
Console.WriteLine("Выполнение стратегии A.");
}
}
public class ConcreteStrategyB : IStrategy
{
public void Execute()
{
Console.WriteLine("Выполнение стратегии B.");
}
}
2. Создаем контекст, использующий стратегию:
public class Context
{
private IStrategy _strategy;
public Context(IStrategy strategy)
{
_strategy = strategy;
}
public void SetStrategy(IStrategy strategy)
{
_strategy = strategy;
}
public void ExecuteStrategy()
{
_strategy.Execute();
}
}
3. Используем паттерн Strategy:
class Program
{
static void Main()
{
Context context = new Context(new ConcreteStrategyA());
context.ExecuteStrategy();
context.SetStrategy(new ConcreteStrategyB());
context.ExecuteStrategy();
}
}
Преимущества использования паттернов проектирования:
1. Повышение повторного использования кода.
2. Улучшение читаемости и поддержки.
3. Гибкость и расширяемость.
Channel name was changed to «C# - Примеры, советы, best practices»
Принципы SOLID — это пять основных принципов объектно-ориентированного программирования, которые помогают создавать гибкий, расширяемый и поддерживаемый код.
SOLID #1. Single Responsibility Principle (SRP) — Принцип единственной ответственности:
- Класс должен иметь только одну причину для изменения, то есть выполнять только одну задачу.
- Пример:
C# - Примеры, советы, best practices
SOLID #1. Single Responsibility Principle (SRP) — Принцип единственной ответственности:
- Класс должен иметь только одну причину для изменения, то есть выполнять только одну задачу.
- Пример:
public class ReportGenerator
{
public void GenerateReport()
{
// Логика генерации отчета
}
}
public class ReportPrinter
{
public void PrintReport(Report report)
{
// Логика печати отчета
}
}
C# - Примеры, советы, best practices
SOLID #2. Open/Closed Principle (OCP) — Принцип открытости/закрытости:
- Классы должны быть открыты для расширения, но закрыты для модификации.
- Пример:
C# - Примеры, советы, best practices
- Классы должны быть открыты для расширения, но закрыты для модификации.
- Пример:
public abstract class Shape
{
public abstract double CalculateArea();
}
public class Circle : Shape
{
public double Radius { get; set; }
public override double CalculateArea()
{
return Math.PI * Radius * Radius;
}
}
public class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
public override double CalculateArea()
{
return Width * Height;
}
}
C# - Примеры, советы, best practices
SOLID #3. Liskov Substitution Principle (LSP) — Принцип подстановки Лисков:
Объекты в программе должны быть заменяемы их экземплярами подклассов без изменения корректности программы.
Пример:
В этом примере класс Ostrich нарушает принцип LSP, так как его метод
C# - Примеры, советы, best practices
Объекты в программе должны быть заменяемы их экземплярами подклассов без изменения корректности программы.
Пример:
public class Bird
{
public virtual void Fly()
{
// Логика полета
}
}
public class Sparrow : Bird
{
public override void Fly()
{
// Логика полета воробья
}
}
public class Ostrich : Bird
{
public override void Fly()
{
throw new NotImplementedException("Страусы не летают");
}
}
В этом примере класс Ostrich нарушает принцип LSP, так как его метод
Fly не имеет смысла. Чтобы соблюсти LSP, можно создать отдельные классы для птиц, которые летают, и для тех, которые не летают.C# - Примеры, советы, best practices
SOLID #4. Interface Segregation Principle (ISP) — Принцип разделения интерфейса:
Клиенты не должны зависеть от интерфейсов, которые они не используют.
Пример:
C# - Примеры, советы, best practices
Клиенты не должны зависеть от интерфейсов, которые они не используют.
Пример:
public interface IPrinter
{
void Print();
}
public interface IScanner
{
void Scan();
}
public class MultiFunctionPrinter : IPrinter, IScanner
{
public void Print()
{
// Логика печати
}
public void Scan()
{
// Логика сканирования
}
}
public class SimplePrinter : IPrinter
{
public void Print()
{
// Логика печати
}
}
C# - Примеры, советы, best practices
👍1
SOLID #5. Dependency Inversion Principle (DIP) — Принцип инверсии зависимостей:
Модули высокого уровня не должны зависеть от модулей низкого уровня. Оба должны зависеть от абстракций.
Пример:
C# - Примеры, советы, best practices
Модули высокого уровня не должны зависеть от модулей низкого уровня. Оба должны зависеть от абстракций.
Пример:
public interface IMessageSender
{
void SendMessage(string message);
}
public class EmailSender : IMessageSender
{
public void SendMessage(string message)
{
// Логика отправки email
}
}
public class Notification
{
private readonly IMessageSender _messageSender;
public Notification(IMessageSender messageSender)
{
_messageSender = messageSender;
}
public void Notify(string message)
{
_messageSender.SendMessage(message);
}
}
// Использование
var emailSender = new EmailSender();
var notification = new Notification(emailSender);
notification.Notify("Hello, World!");
C# - Примеры, советы, best practices
Используйте Null-объекты для предотвращения NullReferenceException
Использование Null-объектов (Null Object Pattern) позволяет избежать распространенной проблемы NullReferenceException, когда вызывается метод или свойство у null-ссылки. Вместо того чтобы проверять на null везде, можно использовать объект, который представляет собой "ничего не делающий" экземпляр класса.
Пример использования Null Object Pattern:
1. Создайте интерфейс и классы для основного и Null-объектов:
2. Используйте NullLogger вместо проверки на null:
Преимущества использования Null Object Pattern:
- Null-объект гарантирует, что вызов метода не приведет к ошибке, так как объект существует.
- Упрощение кода: нет необходимости в каждом месте, где используется объект, проверять его на null.
- Улучшение читаемости: код становится более чистым и читаемым, так как исключаются лишние проверки.
Другие практики для работы с null:
1. Используйте оператор null-условной операции (?.):
2. Используйте оператор null-соответствия (??):
3. Применяйте паттерн "поздней инициализации" (Lazy Initialization):
C# - Примеры, советы, best practices
Использование Null-объектов (Null Object Pattern) позволяет избежать распространенной проблемы NullReferenceException, когда вызывается метод или свойство у null-ссылки. Вместо того чтобы проверять на null везде, можно использовать объект, который представляет собой "ничего не делающий" экземпляр класса.
Пример использования Null Object Pattern:
1. Создайте интерфейс и классы для основного и Null-объектов:
public interface ILogger
{
void Log(string message);
}
public class ConsoleLogger : ILogger
{
public void Log(string message)
{
Console.WriteLine(message);
}
}
public class NullLogger : ILogger
{
public void Log(string message)
{
// Ничего не делает
}
}
2. Используйте NullLogger вместо проверки на null:
public class Service
{
private readonly ILogger _logger;
public Service(ILogger logger)
{
_logger = logger ?? new NullLogger(); // Если передан null, используем NullLogger
}
public void DoWork()
{
_logger.Log("Начало работы.");
// Логика выполнения работы
_logger.Log("Работа завершена.");
}
}
// Использование
class Program
{
static void Main()
{
ILogger logger = new ConsoleLogger(); // Или null
Service service = new Service(logger);
service.DoWork();
}
}
Преимущества использования Null Object Pattern:
- Null-объект гарантирует, что вызов метода не приведет к ошибке, так как объект существует.
- Упрощение кода: нет необходимости в каждом месте, где используется объект, проверять его на null.
- Улучшение читаемости: код становится более чистым и читаемым, так как исключаются лишние проверки.
Другие практики для работы с null:
1. Используйте оператор null-условной операции (?.):
string result = obj?.ToString();
2. Используйте оператор null-соответствия (??):
string message = input ?? "Default message";
3. Применяйте паттерн "поздней инициализации" (Lazy Initialization):
private Lazy<MyClass> _myClass = new Lazy<MyClass>(() => new MyClass());
public MyClass MyClassInstance => _myClass.Value;
C# - Примеры, советы, best practices
👍1
Методы расширения позволяют добавлять новые методы в существующие типы без необходимости изменять их исходный код или создавать производные классы. Они полезны для улучшения читаемости кода и его повторного использования.
Создание
Методы расширения определяются как статические методы в статическом классе. Первый параметр метода указывает к какому типу он применяется и должен быть помечен ключевым словом
Использование
После определения методов расширения их можно вызывать как обычные методы экземпляра.
C# - Примеры, советы, best practices
Создание
Методы расширения определяются как статические методы в статическом классе. Первый параметр метода указывает к какому типу он применяется и должен быть помечен ключевым словом
this.public static class StringExtensions
{
public static bool IsNullOrEmpty(this string str)
{
return string.IsNullOrEmpty(str);
}
public static string Reverse(this string str)
{
char[] charArray = str.ToCharArray();
Array.Reverse(charArray);
return new string(charArray);
}
}
Использование
После определения методов расширения их можно вызывать как обычные методы экземпляра.
using System;
class Program
{
static void Main()
{
string example = "Hello, World!";
// Использование метода IsNullOrEmpty
bool isEmpty = example.IsNullOrEmpty();
Console.WriteLine($"Is string empty: {isEmpty}");
// Использование метода Reverse
string reversed = example.Reverse();
Console.WriteLine($"Reversed string: {reversed}");
}
}
C# - Примеры, советы, best practices
Пример создания методов расширения для коллекций
C# - Примеры, советы, best practices
public static class CollectionExtensions
{
public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> items)
{
foreach (var item in items)
{
collection.Add(item);
}
}
}
// Использование
class Program
{
static void Main()
{
List<int> numbers = new List<int> { 1, 2, 3 };
int[] moreNumbers = { 4, 5, 6 };
// Использование расширяющего метода AddRange
numbers.AddRange(moreNumbers);
Console.WriteLine("Числа:");
foreach (var number in numbers)
{
Console.WriteLine(number);
}
}
}
C# - Примеры, советы, best practices
👍2
Следуйте соглашениям по наименованию (naming conventions).
Использование стандартных соглашений по наименованию делает ваш код более читаемым и понятным для других разработчиков. Вот основные правила:
Классы и методы:
- Имена классов и методов должны использовать PascalCase (каждое слово начинается с заглавной буквы).
- Пример:
Переменные и параметры:
- Имена переменных и параметров должны использовать camelCase (первое слово с маленькой буквы, остальные с заглавной).
- Пример:
Константы:
- Имена констант должны использовать PascalCase и часто начинаются с
- Пример:
C# - Примеры, советы, best practices
Использование стандартных соглашений по наименованию делает ваш код более читаемым и понятным для других разработчиков. Вот основные правила:
Классы и методы:
- Имена классов и методов должны использовать PascalCase (каждое слово начинается с заглавной буквы).
- Пример:
MyClass, CalculateTotalПеременные и параметры:
- Имена переменных и параметров должны использовать camelCase (первое слово с маленькой буквы, остальные с заглавной).
- Пример:
totalAmount, userNameКонстанты:
- Имена констант должны использовать PascalCase и часто начинаются с
k или s для обозначения констант и статических переменных.- Пример:
MaxValue, DefaultTimeoutpublic class Customer
{
private string firstName;
private string lastName;
public Customer(string firstName, string lastName)
{
this.firstName = firstName;
this.lastName = lastName;
}
public string GetFullName()
{
return $"{firstName} {lastName}";
}
}
C# - Примеры, советы, best practices
👍1
#Пример Подсчет количества слов в текстовом файле
Пояснение
• Чтение файла:
• Разделение текста на слова:
• Подсчет слов:
• Обработка исключений для случая, когда файл не найден (
Этот пример демонстрирует основные операции с файлами, работу с текстом и обработку ошибок, что полезно в реальных приложениях для анализа текстовых данных.
C# - Примеры, советы, best practices
using System;
using System.IO;
using System.Linq;
class Program
{
static void Main()
{
string filePath = "example.txt";
try
{
// Считывание всего содержимого файла
string text = File.ReadAllText(filePath);
// Разделение текста на слова
string[] words = text.Split(new char[] { ' ', '\n', '\r', '\t' }, StringSplitOptions.RemoveEmptyEntries);
// Подсчет количества слов
int wordCount = words.Length;
Console.WriteLine($"Количество слов в файле: {wordCount}");
}
catch (FileNotFoundException)
{
Console.WriteLine($"Файл не найден: {filePath}");
}
catch (Exception ex)
{
Console.WriteLine($"Произошла ошибка: {ex.Message}");
}
}
}
Пояснение
• Чтение файла:
File.ReadAllText(filePath) считывает все содержимое файла в строку.• Разделение текста на слова:
text.Split() разбивает текст на слова, используя пробелы, символы новой строки, возврата каретки и табуляции в качестве разделителей. Параметр StringSplitOptions.RemoveEmptyEntries исключает пустые строки из результатов.• Подсчет слов:
words.Length возвращает количество элементов в массиве слов, что соответствует количеству слов в тексте.• Обработка исключений для случая, когда файл не найден (
FileNotFoundException), и общего исключения для других возможных ошибок.Этот пример демонстрирует основные операции с файлами, работу с текстом и обработку ошибок, что полезно в реальных приложениях для анализа текстовых данных.
C# - Примеры, советы, best practices
👍2
Паттерн Репозиторий (Repository Pattern)
Паттерн Репозиторий - это шаблон проектирования, который абстрагирует слой доступа к данным и предоставляет интерфейс для взаимодействия с ними.
Пример использования паттерна Repository:
Создание сущности и контекста данных
Создание интерфейса репозитория
Реализация репозитория
Использование репозитория в сервисе
Пример использования в программе
Преимущества использования паттерна Репозиторий
• Абстракция доступа к данным
Репозиторий абстрагирует логику доступа к данным, обеспечивая четкое разделение ответственности между бизнес-логикой и логикой доступа к данным.
• Улучшение тестируемости
Легко замещать репозиторий моками при тестировании, что упрощает юнит-тестирование.
• Поддержка принципов SOLID
Репозиторий помогает соблюдать принцип единственной ответственности (SRP) и принцип инверсии зависимостей (DIP), улучшая архитектуру приложения.
C# - Примеры, советы, best practices
Паттерн Репозиторий - это шаблон проектирования, который абстрагирует слой доступа к данным и предоставляет интерфейс для взаимодействия с ними.
Пример использования паттерна Repository:
Создание сущности и контекста данных
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
public class AppDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseInMemoryDatabase("TestDb");
}
}
Создание интерфейса репозитория
public interface IProductRepository
{
IEnumerable<Product> GetAll();
Product GetById(int id);
void Add(Product product);
void Update(Product product);
void Delete(int id);
}
Реализация репозитория
public class ProductRepository : IProductRepository
{
private readonly AppDbContext _context;
public ProductRepository(AppDbContext context)
{
_context = context;
}
public IEnumerable<Product> GetAll()
{
return _context.Products;
}
public Product GetById(int id)
{
return _context.Products.Find(id);
}
public void Add(Product product)
{
_context.Products.Add(product);
_context.SaveChanges();
}
public void Update(Product product)
{
_context.Products.Update(product);
_context.SaveChanges();
}
public void Delete(int id)
{
var product = _context.Products.Find(id);
if (product != null)
{
_context.Products.Remove(product);
_context.SaveChanges();
}
}
}
Использование репозитория в сервисе
public class ProductService
{
private readonly IProductRepository _repository;
public ProductService(IProductRepository repository)
{
_repository = repository;
}
public void DisplayAllProducts()
{
var products = _repository.GetAll();
foreach (var product in products)
{
Console.WriteLine($"{product.Id}: {product.Name} - {product.Price:C}");
}
}
}
Пример использования в программе
class Program
{
static void Main()
{
var context = new AppDbContext();
IProductRepository repository = new ProductRepository(context);
var service = new ProductService(repository);
// Добавление продуктов
repository.Add(new Product { Name = "Product 1", Price = 10.99m });
repository.Add(new Product { Name = "Product 2", Price = 20.99m });
// Отображение всех продуктов
service.DisplayAllProducts();
}
}
Преимущества использования паттерна Репозиторий
• Абстракция доступа к данным
Репозиторий абстрагирует логику доступа к данным, обеспечивая четкое разделение ответственности между бизнес-логикой и логикой доступа к данным.
• Улучшение тестируемости
Легко замещать репозиторий моками при тестировании, что упрощает юнит-тестирование.
• Поддержка принципов SOLID
Репозиторий помогает соблюдать принцип единственной ответственности (SRP) и принцип инверсии зависимостей (DIP), улучшая архитектуру приложения.
C# - Примеры, советы, best practices
👍1
Дополнительные советы по использованию асинхронных потоков
• Используйте
Это позволяет асинхронно получать данные по мере их поступления.
• Комбинируйте с другими асинхронными операциями:
Асинхронные потоки можно комбинировать с другими асинхронными операциями, такими как сетевые запросы или операции ввода-вывода, для создания гибких и эффективных решений.
• Обработка ошибок:
Обрабатывайте возможные ошибки внутри асинхронного потока с использованием конструкций
C# - Примеры, советы, best practices
• Используйте
await foreach для итерации по асинхронным потокам:Это позволяет асинхронно получать данные по мере их поступления.
• Комбинируйте с другими асинхронными операциями:
Асинхронные потоки можно комбинировать с другими асинхронными операциями, такими как сетевые запросы или операции ввода-вывода, для создания гибких и эффективных решений.
• Обработка ошибок:
Обрабатывайте возможные ошибки внутри асинхронного потока с использованием конструкций
try-catch, чтобы обеспечить надежность приложения.C# - Примеры, советы, best practices
👍1
Асинхронные потоки (Asynchronous Streams) для обработки данных по мере их поступления
Асинхронные потоки позволяют обрабатывать данные по мере их поступления, что особенно полезно при работе с большими объемами данных или при необходимости асинхронного взаимодействия с внешними источниками данных.
Создание асинхронного метода, возвращающего поток
Чтение данных из асинхронного потока
Преимущества использования асинхронных потоков
• Обработка данных по мере их поступления:
Это улучшает отзывчивость приложения и уменьшает потребление памяти при работе с большими объемами данных.
• Улучшение производительности:
Асинхронные операции не блокируют основной поток, что позволяет продолжать выполнение других задач параллельно.
• Удобство работы с асинхронными источниками данных
Асинхронные потоки упрощают интеграцию с асинхронными источниками данных, такими как сетевые запросы или асинхронные операции ввода-вывода.
C# - Примеры, советы, best practices
Асинхронные потоки позволяют обрабатывать данные по мере их поступления, что особенно полезно при работе с большими объемами данных или при необходимости асинхронного взаимодействия с внешними источниками данных.
Создание асинхронного метода, возвращающего поток
public class DataGenerator
{
public async IAsyncEnumerable<int> GenerateDataAsync()
{
for (int i = 0; i < 10; i++)
{
await Task.Delay(500); // Симуляция асинхронной операции
yield return i;
}
}
}
Чтение данных из асинхронного потока
class Program
{
static async Task Main(string[] args)
{
var dataGenerator = new DataGenerator();
await foreach (var number in dataGenerator.GenerateDataAsync())
{
Console.WriteLine($"Получено число: {number}");
}
}
}
Преимущества использования асинхронных потоков
• Обработка данных по мере их поступления:
Это улучшает отзывчивость приложения и уменьшает потребление памяти при работе с большими объемами данных.
• Улучшение производительности:
Асинхронные операции не блокируют основной поток, что позволяет продолжать выполнение других задач параллельно.
• Удобство работы с асинхронными источниками данных
Асинхронные потоки упрощают интеграцию с асинхронными источниками данных, такими как сетевые запросы или асинхронные операции ввода-вывода.
C# - Примеры, советы, best practices
👍2
Span<T> и Memory<T> для эффективной работы с памятьюSpan<T> и Memory<T> — это структуры, которые позволяют работать с последовательностями данных, такими как массивы, строки и буферы, без необходимости выделения новой памяти и копирования данных. Они особенно полезны для высокопроизводительных приложений, требующих минимизации накладных расходов на управление памятью.Создание и использование Span<T>
using System;
class Program
{
static void Main()
{
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// Создание Span, представляющего часть массива
Span<int> slice = new Span<int>(numbers, 2, 5);
// Изменение данных через Span
for (int i = 0; i < slice.Length; i++)
{
slice[i] *= 2;
}
// Вывод измененных данных
Console.WriteLine(string.Join(", ", numbers));
}
}
Создание и использование Memory<T>
using System;
class Program
{
static void Main()
{
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
// Создание Memory, представляющего часть массива
Memory<int> memory = new Memory<int>(numbers, 2, 5);
// Асинхронная работа с Memory
ProcessMemoryAsync(memory).Wait();
// Вывод измененных данных
Console.WriteLine(string.Join(", ", numbers));
}
static async Task ProcessMemoryAsync(Memory<int> memory)
{
// Использование Memory в асинхронном контексте
for (int i = 0; i < memory.Length; i++)
{
memory.Span[i] *= 2;
await Task.Delay(100); // Симуляция асинхронной операции
}
}
}
Преимущества использования
Span<T> и Memory<T>• Эффективное использование памяти
Span<T> и Memory<T> позволяют работать с подмножествами массивов и других последовательностей данных без копирования данных, что снижает накладные расходы на память.• Высокая производительность
Работа с
Span<T> и Memory<T> обходится дешевле по сравнению с традиционными методами, такими как создание новых массивов или списков.• Безопасность
Span<T> и Memory<T> встроены в систему типов C#, что обеспечивает безопасность работы с памятью, предотвращая доступ к неинициализированным или освобожденным областям памяти.Когда использовать
Span<T> и Memory<T>• Обработка строк и текстов: Для работы с подстроками и частями текстов без выделения новых строк.
• Буферы ввода-вывода: Для работы с частями буферов данных при чтении или записи данных.
• Оптимизация производительности: В высокопроизводительных приложениях, где важно минимизировать накладные расходы на управление памятью.
C# - Примеры, советы, best practices
👍2
varКогда использовать и когда НЕ использовать
Ключевое слово
var позволяет компилятору автоматически выводить тип переменной из присваиваемого значения. Это может улучшить читаемость кода, делая его более компактным, особенно когда тип переменной очевиден из контекста.Когда использование `var` оправдано:
var userName = "Евгений Данилов"; // Компилятор выводит тип string
var userAge = 30; // Компилятор выводит тип int
var users = new List<string>(); // Компилятор выводит тип List<string>
Когда лучше явно указать тип:
// Когда тип не очевиден из контекста или улучшает читаемость кода
Dictionary<string, List<int>> userScores = new Dictionary<string, List<int>>();
Преимущества использования
var:• Уменьшение избыточности:
var позволяет избежать избыточного повторения типов, делая код более компактным.• Улучшение читаемости:
В некоторых случаях использование
var делает код легче читаемым, особенно при работе с длинными именами типов.• Упрощение рефакторинга:
При изменении типа переменной, использование
var позволяет избежать необходимости обновления всех объявлений переменных.Когда лучше избегать использования
varНеочевидный тип:
Если тип переменной не очевиден из присваиваемого значения, лучше явно указать тип.
// Плохое использование var
var result = CalculateResult();
// Лучше явно указать тип
int result = CalculateResult();
Сложные выражения:
Если выражение слишком сложное и тип переменной не сразу очевиден, лучше явно указать тип.
// Плохое использование var
var usersGroupedByAge = users.GroupBy(u => u.Age);
// Лучше явно указать тип
IEnumerable<IGrouping<int, User>> usersGroupedByAge = users.GroupBy(u => u.Age);
C# - Примеры, советы, best practices
👌1🤣1
IQueryable<T>Правильное использование
IQueryable<T> позволяет писать более гибкий и производительный код для работы с запросами к базам данных.•
IQueryable<T> используется для запросов к удаленным источникам данных, таким как базы данных.• Подходит для LINQ to SQL и LINQ to Entities (Entity Framework).
• Выполняет все операции фильтрации, сортировки и агрегации на серверной стороне.
• Поддерживает методы расширения LINQ и дополнительные методы, специфичные для запросов к базам данных, такие как
Include.Пример использования
IQueryable<T> с Entity Framework:using System;
using System.Linq;
using Microsoft.EntityFrameworkCore;
public class ApplicationDbContext : DbContext
{
public DbSet<Product> Products { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer("YourConnectionStringHere");
}
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
class Program
{
static void Main()
{
using (var context = new ApplicationDbContext())
{
// Использование IQueryable для запроса к базе данных
IQueryable<Product> expensiveProducts = context.Products.Where(p => p.Price > 1000);
foreach (var product in expensiveProducts)
{
Console.WriteLine($"Продукт: {product.Name}, Цена: {product.Price}");
}
}
}
}
C# - Примеры, советы, best practices
👍1
Использование
Асинхронные потоки данных (
Создание асинхронного метода, возвращающего
Чтение данных из асинхронного потока:
Преимущества
• Эффективная обработка данных:
Асинхронные потоки позволяют обрабатывать данные по мере их поступления, что уменьшает задержки и повышает отзывчивость приложения.
• Меньшая нагрузка на память:
Данные обрабатываются по мере их поступления, что снижает потребление памяти, так как не требуется хранить весь набор данных в памяти одновременно.
• Удобная работа с асинхронными источниками данных:
Асинхронные потоки упрощают интеграцию с асинхронными API, такими как сетевые запросы или асинхронные операции ввода-вывода.
C# - Примеры, советы, best practices
async/await вместе с IAsyncEnumerable<T> для обработки асинхронных потоков данныхАсинхронные потоки данных (
IAsyncEnumerable<T>) позволяют обрабатывать данные по мере их поступления, что особенно полезно для работы с потоками данных, получаемых из сетевых ресурсов, файловых систем или других асинхронных источников.Создание асинхронного метода, возвращающего
IAsyncEnumerable<T>:using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public class DataGenerator
{
public async IAsyncEnumerable<int> GenerateNumbersAsync()
{
for (int i = 0; i < 10; i++)
{
await Task.Delay(500); // Симуляция асинхронной операции
yield return i;
}
}
}
Чтение данных из асинхронного потока:
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
var dataGenerator = new DataGenerator();
await foreach (var number in dataGenerator.GenerateNumbersAsync())
{
Console.WriteLine($"Получено число: {number}");
}
}
}
Преимущества
IAsyncEnumerable<T>:• Эффективная обработка данных:
Асинхронные потоки позволяют обрабатывать данные по мере их поступления, что уменьшает задержки и повышает отзывчивость приложения.
• Меньшая нагрузка на память:
Данные обрабатываются по мере их поступления, что снижает потребление памяти, так как не требуется хранить весь набор данных в памяти одновременно.
• Удобная работа с асинхронными источниками данных:
Асинхронные потоки упрощают интеграцию с асинхронными API, такими как сетевые запросы или асинхронные операции ввода-вывода.
C# - Примеры, советы, best practices
Обработка ошибок внутри асинхронного метода
C# - Примеры, советы, best practices
public async IAsyncEnumerable<int> GenerateNumbersAsync()
{
for (int i = 0; i < 10; i++)
{
try
{
await Task.Delay(500); // Симуляция асинхронной операции
yield return i;
}
catch (Exception ex)
{
Console.WriteLine($"Ошибка: {ex.Message}");
yield break;
}
}
}
C# - Примеры, советы, best practices