Здравствуйте, у меня очень простой вопрос(мне думается). Создал класс, а нем метод(см. ниже). Каким образом можно избавиться от switch. Вроде бы так делать не желательно, это же ООП язык и нужно применять, что то более грамотное(полиморфизм и все что с этим связано). Если количество выборок увеличится, то switch станет ну очень неприятным. Есть ли общепринятые методики для таких вот фрагментов?
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, у меня очень простой вопрос(мне думается). Создал класс, а нем метод(см. ниже). Каким образом можно избавиться от switch. Вроде бы так делать не желательно, это же ООП язык и нужно применять, что то более грамотное(полиморфизм и все что с этим связано). Если количество выборок увеличится, то switch станет ну очень неприятным. Есть ли общепринятые методики для таких вот фрагментов? А>
private Dictionary<string, Action> _d = new Dictionary<string, Action>
{
{ "txt", ParseTXT },
......
}
А>public void Check_files(string inFile)
А> {
А> Action actor;
if(_d.TryGetValue(nFile, out actor))
actor();
else// не нашли
А> }
А>
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, у меня очень простой вопрос(мне думается). Создал класс, а нем метод(см. ниже). Каким образом можно избавиться от switch. Вроде бы так делать не желательно, это же ООП язык и нужно применять, что то более грамотное(полиморфизм и все что с этим связано). Если количество выборок увеличится, то switch станет ну очень неприятным. Есть ли общепринятые методики для таких вот фрагментов?
Какое такое ООП?
using System;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
var str = new string[] { "txt", "conf", "TXT" };
foreach (var s in str)
{
Parse(s);
}
}
static void Parse(string s)
{
var bindingFlags = BindingFlags.Static | BindingFlags.InvokeMethod | BindingFlags.IgnoreCase | BindingFlags.NonPublic;
typeof(Program).InvokeMember("Parse" + s, bindingFlags, null, null, null);
}
static void ParseTxt()
{
Console.WriteLine("ParseTxt");
}
static void ParseConf()
{
Console.WriteLine("ParseConf");
}
}
}
Здравствуйте, <Аноним>, Вы писали:
А>Здравствуйте, у меня очень простой вопрос(мне думается). Создал класс, а нем метод(см. ниже). Каким образом можно избавиться от switch.
А зачем?
А> Вроде бы так делать не желательно, это же ООП язык и нужно применять, что то более грамотное(полиморфизм и все что с этим связано). Если количество выборок увеличится, то switch станет ну очень неприятным.
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, у меня очень простой вопрос(мне думается). Создал класс, а нем метод(см. ниже). Каким образом можно избавиться от switch. Вроде бы так делать не желательно, это же ООП язык и нужно применять, что то более грамотное(полиморфизм и все что с этим связано). Если количество выборок увеличится, то switch станет ну очень неприятным. Есть ли общепринятые методики для таких вот фрагментов?
Чтобы ответить на твой вопрос, надо понять, чего ты хочешь в будущем от этого кода.
I>Насколько оправдано здесь применение такой тяжелой структуры данных как Dictionary?
В чем тяжесть?
ИМХО
С одной стороны, инициализация Dictionary выглядит компактнее, чем switch с тем же количеством ветвлений.
С другой стороны, switch гибче, если вдруг сигнатура ParseTXT резко станет отличаться от какого-нибудь ParsePDF.
Здравствуйте, Аноним, Вы писали:
А>Вроде бы так делать не желательно, это же ООП язык и нужно применять, что то более грамотное(полиморфизм и все что с этим связано).
Полнейший бред.
Switch это элегантная, лекго читаемая и изменяемая конструкция.
Здравствуйте, Аноним, Вы писали:
А>Каким образом можно избавиться от switch. Есть ли общепринятые методики для таких вот фрагментов?
Есть паттерны ага.
У меня один проект на свитчах, намучался, второй уже писал используя делегаты и функции в виде связанного списка (каждая функция это один из шагов свитча, они выполняются и передают управление дальше, по "траектории", которая определяет поток выполнения).
Первое что можно сделать — это ввести enum для типов:
enum FileType {Txt, Conf, Log, Unknown};
Далее можно пойти по пути TypeConverter — вызывать все обработчики, а уже пусть обработчики решают "могут" они или не могут. Типа:
Возможно есть более кавайное решение для группировки парсеров. В данном случае при добавление нового парсера его нужно происнтансцинировать и добавить в список _list, дальше все работает автоматически.. Оо .. (почти ООП)
Здравствуйте, Sinatr, Вы писали:
S>Далее можно пойти по пути TypeConverter — вызывать все обработчики, а уже пусть обработчики решают "могут" они или не могут. Типа:
Что делать в случае, когда два парсера заявят о том, что они могут разобрать входные данные?
/* иЗвиНите зА неРовнЫй поЧерК */
Re[2]: switch убрать.
От:
Аноним
Дата:
11.06.13 11:57
Оценка:
Кстати, а можно ли в Dictionary добавить, не вызов функции, а непосредственно код, что то в стиле анонимных функций как в perl?
Здравствуйте, hardcase, Вы писали:
H>Что делать в случае, когда два парсера заявят о том, что они могут разобрать входные данные?
Возможно именно это как раз и было нужно, раз уж написали 2 парсера? Ведь неизвестно чем является результат работы парсеров. Конфликт по идее можно разрешить просто: первый кто может разобрать (или последний) — разбирает.
А вообще да, еще одно необходимое условие для добавки нового парсера — это добавить в enum понимаемый им FileType. Как-то неподумалось.
А меня Dictionary всегда смущает. Ведь назначение Dictionary — поиск в больших массивах данных, ну при нескольких сотнях вхождений хотя бы. А тут простая таблица соответствия нужна.
В Reflection тоже не вижу ничего плохого, в более-менее нормальных (особенно UI) приложениях он используется повсеместно, если даже неявно, тем более динамическую привязку можно оптимизировать. Я бы не стал называть использование Reflection обременением за исключением специфичных областей по перелопачиванию огромных массивов информации на скорость.
Соглашение об именовании — это вообще один из столпов программирования.
А для своего личного использования пример с Dictionary (без Reflection) я бы переписал примерно так (для меня гораздо читабельнее, и было бы гораздо легче понять впоследствии, что тут и для чего):
using System;
using System.Linq;
using System.Reflection;
namespace ConsoleApplication1
{
static class Program
{
static ParserEntry[] _parsers = new ParserEntry[] {
"txt".ParseWith(ParseTxt),
"conf".ParseWith(ParseConf)
};
static ParserEntry ParseWith(this string inFile, Action action)
{
return new ParserEntry() { InFile = inFile, Action = action };
}
struct ParserEntry
{
public string InFile;
public Action Action;
}
static void Main(string[] args)
{
var str = new string[] { "txt", "conf", "TXT" };
foreach (var s in str)
{
_parsers.Single(p => string.Compare(s, p.InFile, true) == 0).Action();
}
}
static void ParseTxt()
{
Console.WriteLine("ParseTxt");
}
static void ParseConf()
{
Console.WriteLine("ParseConf");
}
}
}
Здравствуйте, AndrewVK, Вы писали:
I>>А меня Dictionary всегда смущает. Ведь назначение Dictionary — поиск в больших массивах данных AVK>Назначение Dictionary — ассоциативный массив.
Посредством вычисления хеш-значения. Какова целесообразность этого для десяти постоянных значений?
AVK>Посмотри как компилятор C# компилирует строковый свитч хотя бы вариантов на 10 — сильно удивишься.
Dictionary Но это ведь не значит, что оптимизатор прав, создавая хэш для десяти значений, когда можно обойтись двухстолбцовой таблицей.
I>>в более-менее нормальных (особенно UI) приложениях он используется повсеместно AVK>Использоваться он, конечно, используется, то там где можно без него, нужно без него.
У ТС, могу предположить, как раз тот случай. Вполне вероятно парсеры у него подключаются как плагины через конфигурационный файл
Здравствуйте, Ilinichev, Вы писали:
I>Посредством вычисления хеш-значения. Какова целесообразность этого для десяти постоянных значений?
Вполне себе.
AVK>>Посмотри как компилятор C# компилирует строковый свитч хотя бы вариантов на 10 — сильно удивишься. I>Dictionary Но это ведь не значит, что оптимизатор прав
Ну конечно, тебе виднее
I>, создавая хэш для десяти значений, когда можно обойтись двухстолбцовой таблицей.
Что такое "двухстолбцовая таблица"?
AVK>>Использоваться он, конечно, используется, то там где можно без него, нужно без него. I>У ТС, могу предположить, как раз тот случай.
Это почему это? Его задача хорошо решается и без рефлекшена.
I> Вполне вероятно парсеры у него подключаются как плагины через конфигурационный файл
Да да, а потом варианты хардкодятся в switch
... << RSDN@Home 1.2.0 alpha 5 rev. 99 on Windows 8 6.2.9200.0>>
Здравствуйте, Аноним, Вы писали:
А> Есть ли общепринятые методики для таких вот фрагментов?
Ну вот так например:
class Program
{
static void Main(string[] args)
{
var magicChecker = new MagicCheckerBuilder()
.WithParser("txt", new TxtParser())
.WithParser("conf", new ConfParser())
.WithParser("log", new LogParser())
.Build();
var yourService = new YourService(magicChecker);
yourService.CheckFiles("conf");
}
}
public class YourService
{
private readonly MagicChecker _magicChecker;
public YourService(MagicChecker magicChecker)
{
_magicChecker = magicChecker;
}
public void CheckFiles(string inFile)
{
_magicChecker.MagicallyCheck(inFile);
}
}
public class MagicChecker
{
private readonly IDictionary<string, IParser> _parsers;
public MagicChecker(IDictionary<string, IParser> parsers)
{
_parsers = parsers;
}
public void MagicallyCheck(string inFile)
{
var haveNoIdeaWhatToDo = !_parsers.ContainsKey(inFile);
if (haveNoIdeaWhatToDo)
{
throw new Exception("Have no idea what to do");
}
var parser = _parsers[inFile];
parser.Parse();
}
}
public class MagicCheckerBuilder
{
private readonly IDictionary<string, IParser> _parsers = new Dictionary<string, IParser>();
public MagicCheckerBuilder WithParser(string inFile, IParser parser)
{
_parsers.Add(inFile, parser);
return this;
}
public MagicChecker Build()
{
return new MagicChecker(_parser);
}
}
public interface IParser
{
void Parse();
}
public class TxtParser : IParser
{
public void Parse()
{
throw new NotImplementedException();
}
}
public class ConfParser : IParser
{
public void Parse()
{
throw new NotImplementedException();
}
}
public class LogParser : IParser
{
public void Parse()
{
throw new NotImplementedException();
}
}
I>>>А меня Dictionary всегда смущает. Ведь назначение Dictionary — поиск в больших массивах данных AVK>>Назначение Dictionary — ассоциативный массив. I>Посредством вычисления хеш-значения. Какова целесообразность этого для десяти постоянных значений?
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания http://rsdn.ru/Info/rules.xml
AVK>Написать на коленке типизированные недолго, благо ListDictionary примитивен донельзя. Вот только будет ли выигрыш заметный — большой вопрос.
Зависит от того сколько раз будет вызывать код с ListDictionary, если один раз в день, то выигрыша не будет.
Если 1000 раз в сек, то выигрыш будет.
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания http://rsdn.ru/Info/rules.xml
Здравствуйте, igor-booch, Вы писали:
AVK>>Написать на коленке типизированные недолго, благо ListDictionary примитивен донельзя. Вот только будет ли выигрыш заметный — большой вопрос. IB>Зависит от того сколько раз будет вызывать код с ListDictionary, если один раз в день, то выигрыша не будет. IB>Если 1000 раз в сек, то выигрыш будет.
Учитывая что у ТС — там парсинг лога / кофиг, файловый IO — этой разницы не будет видно даже в электронный микроскоп.
Здравствуйте, fddima, Вы писали:
AVK>>>Написать на коленке типизированные недолго, благо ListDictionary примитивен донельзя. Вот только будет ли выигрыш заметный — большой вопрос. IB>>Зависит от того сколько раз будет вызывать код с ListDictionary, если один раз в день, то выигрыша не будет. IB>>Если 1000 раз в сек, то выигрыш будет. F> Учитывая что у ТС — там парсинг лога / кофиг, файловый IO — этой разницы не будет видно даже в электронный микроскоп.
Боюсь вас огорчить, но электронный микроскоп не профайлит
Здравствуйте, Ilinichev, Вы писали:
F>> Учитывая что у ТС — там парсинг лога / кофиг, файловый IO — этой разницы не будет видно даже в электронный микроскоп. I>Боюсь вас огорчить, но электронный микроскоп не профайлит
А профайлить и не надо, что бы понять, что методы Parse* будут занимать 99.9% времени.
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, у меня очень простой вопрос(мне думается). Создал класс, а нем метод(см. ниже). Каким образом можно избавиться от switch. Вроде бы так делать не желательно, это же ООП язык и нужно применять, что то более грамотное(полиморфизм и все что с этим связано). Если количество выборок увеличится, то switch станет ну очень неприятным. Есть ли общепринятые методики для таких вот фрагментов?
Если с т.з. именно ООП то по идее так
////////
// ИНТЕРФЕЙС И ФАБРИКА ДЛЯ СОЗДАНИЯ ОБЪЕКТОВ
/////////public interface IParser
{
void Parse();
}
public static class ParserFactory
{
// Эту штуку можно динамически создавать из app.config напримерstatic ReadOnlyDictionary<string,Type> _parsersNameTypeMap
{
{"txt", typeof(TextParser)}
{"cfg", typeof(ConfigParser)}
}
public static IParser CreateFromExtension( string extension )
{
if (!_parsersNameTypeMap.ContainsKey(extension))
throw new ArgumentException("Неизвестное расширение '" + extension + "'" );
var parser = Activator.CreateInstance( _parserNameTypeMap[ extension ] ) as IParser;
if ( parser == null )
throw new ArgumentException("Для расширения '" + extension + "' задан тип не совместимый с интерфейсом IParser");
return parser;
}
}
/////////////////
// Описание различных алгоритмов парсинга
/////////////////public class TextParser : IParser
{
void Parse()
{
//....
}
}
public class ConfigParser : IParser
{
void Parse()
{
}
}
Здравствуйте, igor-booch, Вы писали:
IB>Зависит от того сколько раз будет вызывать код с ListDictionary, если один раз в день, то выигрыша не будет. IB>Если 1000 раз в сек, то выигрыш будет.
Дабы закончить свою мысль. Всё дело в если. В ListDictionary и их аналогах есть свои нюансы как и в обычном Dictionary. У Dictionary будет несколько большая косвенность, зато оно даст константное среднее время доступа, а ListDictionary — никогда. Если "1000 раз в сек" спрашивать 10-ый элемент? Но это ерунда.
А, если у нас 3-5 варианта выбора. А, если мы накладываем ограничение, что поданная строка должна быть пропущена через таблицу символов (а-ля интернирована), то банальный if-else с простым сравнением ссылок порвёт любой вариант Dictionary.
Ещё не много если? А если у нас парсер парсит лог секунду (или скажем поближе к реальности — минуты две), толку то.
Микробенчмарки не только нынче не в моде, а ещё и полностью бесполезны, т.к. доступ к памяти — вещь очень неконстантная и зависит собственно от загрузки хоста. Все остальные критерии ничего не значит, код должен быть понятен и прост насколько это возможно (при прочих равных показателях).
Лучше вот тут бы предложил http://www.rsdn.ru/forum/dotnet/5196714.1
Отвечайте на это сообщение, только если у Вас хорошее настроение и в Вашем ответе планируются только конструктивные вопросы и замечания http://rsdn.ru/Info/rules.xml
Здравствуйте, igor-booch, Вы писали:
S>>У меня один проект на свитчах, намучался IB>А в чем заключались мучения?
Сначала все было хорошо, потом появились case внутри которых был довольно увесистый блок кода (а выносить в отдельную функцию было накладно, т.к. много локальных переменных использовалось в самом коде), потом появилась необходимость заводить локальные и глобальные переменные для определенных case-ов, потом появилась необходимость выполнить несколько свитчей за раз и вернутся (что-то вроде вызова подпрограммы, выполняющей шаг, который используется в нескольких местах) и тд.
Новый проект писал с делегатами.. стало как-то красиво и легко вдруг (именно сапортить легко). Словами не обьяснить, понимание приходит когда количество case-ов за 50.
Здравствуйте, Sinatr, Вы писали:
S>Сначала все было хорошо, потом появились case внутри которых был довольно увесистый блок кода (а выносить в отдельную функцию было накладно, т.к. много локальных переменных использовалось в самом коде), потом появилась необходимость заводить локальные и глобальные переменные для определенных case-ов, потом появилась необходимость выполнить несколько свитчей за раз и вернутся (что-то вроде вызова подпрограммы, выполняющей шаг, который используется в нескольких местах) и тд.
И как так выходит, что выносить в отдельную функцию — накладно, а с делегатом(по сути — такой же функцией только вид сбоку) — красиво и легко?
Здравствуйте, Yoriсk, Вы писали:
Y>И как так выходит, что выносить в отдельную функцию — накладно, а с делегатом(по сути — такой же функцией только вид сбоку) — красиво и легко?
Это уже придирки. Но если смотреть на switch/case в любом виде (будь там код или вызовы отдельных функций), то это больше похоже на кашу, в которой сложно разобраться (когда необходимо внести изменения через год или вообще другому человеку). С функциями и делегатами выглядит более элегантно что ли.
Понятно, что switch/case нужны и полезны, но в определенных случаях ими пользоваться неудобно. Взять пример ТС и 50 парсеров — получится каша. Если много чего-то "похожего", то программист всегда предпочтет организовать это в цикле. Исключение — если нужна оптимизация по скорости или индусский код.