Всё больше наших коллег на практике предпочитают избегать применения операторов if
. Эти условия, по их мнению, усложняют и прибавляют багов в наши приложения.
Но условия лежат в основе программирования, и мы не можем полностью избавиться от операторов if — мы можем лишь сократить их использование.
Словарь — это структура данных, предназначенная для хранения группы объектов. Он может использоваться для маппинга, кэша в оперативной памяти, таблиц и т.д. Объекты хранятся в нём как коллекция пар ключ/значение, что очень удобно и характерно для разных объектно-ориентированных языков программирования. Таким же образом — в виде пар ключ/значение — могут быть заданы делегаты.
Что такое делегат?
«Делегат — это объект, который ссылается на метод. Или даже можно сказать, что это переменная ссылочного типа, которая содержит ссылку на методы. Делегаты в C# схожи с указателем на функцию в C/C++. Он помогает определить, какой метод должен вызываться при срабатывании события».
Есть два типа делегатов, которые нужны для наших примеров: Actionи Func. Action
используется для методов void, а Func
— для методов возвращаемого типа return.
Например:
static void Main(string[] args)
{
Dictionary<string, Action> dict = new Dictionary<string, Action>();
dict.Add("foo", bar);
dict["foo"].Invoke();
Console.WriteLine("World");
Console.ReadLine();
}
static void bar()
{
Console.WriteLine("Hello");
}
Здесь определяется словарь с типом <string, Action>
и к нему добавляется элемент, а затем выполняется метод для вызова dict[“foo”]
. Можем использовать dict[“foo”]()
как альтернативу dict[“foo”].Invoke()
.
Результат:
Какой полезный метод, правда?:)
ПримерПредположим, у нас есть модуль генерации отчётов, который создаёт периодические отчёты: ежедневные, еженедельные, ежемесячные, ежегодные и т.д. Причём они не содержат параметрического или возвращаемого типа.
Сначала он выглядит вот так:
using System;
using System.Collections.Generic;
namespace DictionaryTraining
{
public class Program
{
static void Main(string[] args)
{
Reporter reporter = new Reporter();
ReportType reportType = ReportType.Monthly;
PrepareReport(reportType);
Console.ReadLine();
}
private static void PrepareReport(ReportType reportType)
{
Reporter reporter = new Reporter();
if (reportType == ReportType.Daily)
{
reporter.GetDailyReport();
}
else if (reportType == ReportType.Weekly)
{
reporter.GetWeeklyReport();
}
else if (reportType == ReportType.Monthly)
{
reporter.GetMonthlyReport();
}
else if (reportType == ReportType.Annual)
{
reporter.GetAnnualReport();
}
}
}
public class Reporter
{
public void GetDailyReport()
{
Console.WriteLine("Daily report is preparing...");
}
public void GetWeeklyReport()
{
Console.WriteLine("Weekly report is preparing...");
}
public void GetMonthlyReport()
{
Console.WriteLine("Monthly report is preparing...");
}
public void GetAnnualReport()
{
Console.WriteLine("Annual report is preparing...");
}
}
public enum ReportType
{
Daily,
Weekly,
Monthly,
Annual
}
}
Теперь у нас есть класс Reporter
с разными методами для подготовки отчётов.
Для вызова определённого метода в соответствии с типом каждого отчёта используется метод PrepareReport
. Для этого будет задействовано множество операторов if-else
.
Ну а мы попробуем вместо них использовать словарь и делегаты:
using System;
using System.Collections.Generic;
namespace DictionaryTraining
{
public class Program
{
static Dictionary<ReportType, Action> dictReports = new Dictionary<ReportType, Action>();
static void Main(string[] args)
{
Reporter reporter = new Reporter();
dictReports.Add(ReportType.Daily, new Action(reporter.GetDailyReport));
dictReports.Add(ReportType.Weekly, new Action(reporter.GetWeeklyReport));
dictReports.Add(ReportType.Monthly, new Action(reporter.GetMonthlyReport));
dictReports.Add(ReportType.Annual, new Action(reporter.GetAnnualReport));
dictReports[ReportType.Weekly]();
Console.ReadLine();
}
}
public class Reporter
{
public void GetDailyReport()
{
Console.WriteLine("Daily report is preparing...");
}
public void GetWeeklyReport()
{
Console.WriteLine("Weekly report is preparing...");
}
public void GetMonthlyReport()
{
Console.WriteLine("Monthly report is preparing...");
}
public void GetAnnualReport()
{
Console.WriteLine("Annual report is preparing...");
}
}
enum ReportType
{
Daily,
Weekly,
Monthly,
Annual
}
}
Словарь с типом <ReportType, Action>
будет использоваться в качестве делегата, а методы создания отчёта будут вызываться без необходимости проверять тип каждого отчёта.
На мой взгляд, это более читаемый и лёгкий в сопровождении код. Меньше строк — больше ясности.
Делегаты с возвращаемым типомЕсли методы для подготовки отчётов имеют возвращаемый тип, должен использоваться делегат Func
(но все методы должны иметь те же параметрические и возвращаемый типы).
Вот так:
using System;
using System.Collections.Generic;
namespace DictionaryTraining
{
public class Program
{
static Dictionary<ReportType, Func<int, string>> dictReports = new Dictionary<ReportType, Func<int, string>>();
static void Main(string[] args)
{
Reporter reporter = new Reporter();
dictReports.Add(ReportType.Daily, new Func<int, string>(reporter.GetDailyReport));
dictReports.Add(ReportType.Weekly, new Func<int, string>(reporter.GetWeeklyReport));
dictReports.Add(ReportType.Monthly, new Func<int, string>(reporter.GetMonthlyReport));
dictReports.Add(ReportType.Annual, new Func<int, string>(reporter.GetAnnualReport));
dictReports[ReportType.Daily](60);
Console.ReadLine();
}
}
public class Reporter
{
public string GetDailyReport(int dayOfYear)
{
Console.WriteLine("Daily report is preparing...");
return "report of day: " + dayOfYear;
}
public string GetWeeklyReport(int weekOfYear)
{
Console.WriteLine("Weekly report is preparing...");
return "report of week: " + weekOfYear;
}
public string GetMonthlyReport(int monthOfYear)
{
Console.WriteLine("Monthly report is preparing...");
return "report of month: " + monthOfYear;
}
public string GetAnnualReport(int year)
{
Console.WriteLine("Annual report is preparing...");
return "report of year: " + year;
}
}
enum ReportType
{
Daily,
Weekly,
Monthly,
Annual
}
}
И вот результат:
А сколько ещё есть вариантов применения словаря с точки зрения роста показателей производительности!
Перевод статьи Muhammed Hilmi Koca: Effective Dictionary Usage(C#): Avoid If Statements
Комментарии