Пример 3 . Запрос коллекций
Задача: найти все элементы списка, соответствующие заданным критериям (цена товара превышает 10 $)
Реализация (С# 1). Перебор, проверка, отображение
Перебор каждого элемента, его проверка и отображение
using System.Collections;
static void Main(string[] args)
{
ArrayList list = Product.GetSampleProducts();
foreach (Product product in list)
Console.WriteLine(product.ToString());
Console.WriteLine();
foreach (Product product in list)
if (product.Price > 10m)
{
Console.WriteLine(product.ToString());
}
Console.Read();
}
Ограничения/недостатки:
Из-за вложения инструкций друг в друга появляется зависимость между задачами такими как перебор, проверка, вывод.
Реализация (С# 2). Разделение проверки и отображения.
Создание делегатов testиprint, которые впоследствии передаются в соответствующий метод
using System.Collections.Generic;
static void Main(string[] args)
{
List<Product> list = Product.GetSampleProducts();
Action<Product> print = Console.WriteLine; // преобразование группы методов (технология v2)
list.ForEach(print);
Console.WriteLine();
Predicate<Product> test = delegate(Product p) { return p.Price > 10m; };
List<Product> matches = list.FindAll(test);
matches.ForEach(print);
Console.Read();
}
Упрощенная запись, абсолютно аналогичная версии выше:
static void Main(string[] args)
{
List<Product> list = Product.GetSampleProducts();
list.ForEach(Console.WriteLine);
Console.WriteLine();
list.FindAll(delegate(Product p) { return p.Price > 10m; }).ForEach(Console.WriteLine);
Console.Read();
}
Реализация (С# 3). Проверка при помощи лямбра-выражения
static void Main(string[] args)
{
List<Product> list = Product.GetSampleProducts();
list.ForEach(Console.WriteLine);
Console.WriteLine();
foreach (Product product in list.Where(p => p.Price > 10))
{
Console.WriteLine(product);
}
Console.Read();
}
Эволюция:
Жесткая связь между условием и действием
Условие отдельно от действия; анонимные методы упрощают делегаты
Лямбда-выражение упрощает условие для чтения
Пример 4 . Обработка отсутствия данных
В списке товаров могут быть представлены товары подлежащие реализации (цена их известна), а также товары, которые только планируют завезти (цена их реализации пока под вопросом). Так тип decimalне является ссылочным, то значение типаnullиспользовать нельзя.
Реализация (С# 1). Рекомендации к решению задачи
Создать оболочку ссылочного типа вокруг типа decimal.
Создать отдельный логический флаг, указывающий, известна ли цена (bool isPriceKnown)
Использовать для неизвестной цены значение decimal.MinValue
Реализация (С# 2). Структура Nullable<T>.
decimal? price;
public decimal? Price
{
get { return price; }
private set { price = value; }
}
public Product(string name, decimal? price)
{
Name = name;
Price = price;
}
Реализация (С# 3). Отображение продуктов с неизвестной ценой
static void Main(string[] args)
{
List<Product> list = Product.GetSampleProducts();
list.ForEach(Console.WriteLine);
Console.WriteLine();
foreach (Product product in list.Where(p => p.Price ==null))
{
Console.WriteLine(product);
}
Console.Read();
}
Реализация (С# 4). Необязательные параметры и значения по умолчанию
public Product(string name, decimal? price = null)
{
this.name = name;
this.price = price;
}
Product p = new Product(“Unreleased product”);
Эволюция:
(C#1)Выбор между дополнительной работой по поддержке флага, изменением семантики ссылочного типа или применением специального значения
(С#2/3) Типы данных, допускающие значение null
(C#4)Возможность создания необязательных параметров