Эффективное использование словаря (C#) как альтернатива оператору If


Всё больше наших коллег на практике предпочитают избегать применения операторов 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


Поделиться статьей:


Вернуться к статьям

Комментарии

    Ничего не найдено.