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



Книга Эффективное использование словаря (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
}
}

И вот результат:



А сколько ещё есть вариантов применения словаря с точки зрения роста показателей производительности!


438   0  

Comments

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