В общем, подискутировав немного на тему Exception'ов -- мое любопытство к теме только увеличилось. К сожалению темы быстро перерождаются во флейм, участники быстро перестают слушать друг-друга.
По этому я решил подойти к вопросу с практической точки зрения: поменьше слов, побольше кода.
И придумал вот такой максимально простой тест (буквально 7 строчек кода внутри функции) для выявления человека, который действительно лучше всех умеет работать с исключениями. Не на словах а на деле. Вот сам код:
using System;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Введите имя счетчика и нажмите ENTER:");
string fileName = Console.ReadLine();
// Если файла не существует -- нужно создать.
// Может не быть доступа к файлу
// Имя счетчика может быть некорректным -- с этим что-то нужно сделатьstring text = File.ReadAllText(fileName);
int count = int.Parse(text);
Console.WriteLine("Значение счетчика: ", count);
count++;
// Может быть запрещена запись в файл
File.WriteAllText(fileName, count.ToString());
}
}
}
После запуска программы пользователь должен ввести название счетчика. Программа должна получить текущий номер из файла счетчика, отобразить его пользователю и записать в файл инкремент. Оставить в виде консольного приложения для упрощения и наглядности.
Программа должна быть не демо, а реальной. Т.е. считайте что вы пишите реальный проект, которым будут пользоваться тысячи людей. Все должно быть реальным, без всяких упрощений. Единственное что можно упростить для наглядности -- работу с ресурсами -- т.к. файл ресурсов не очень читабелен -- можно заменить его на класс C# с публичными полями.
Язык интерфейса можно использовать либо русский либо английский. Сообщения должны быть 2-х уровней: 1 для неопытного пользователя и 1 для продвинутого пользователя (т.е. детали).
А в голосовании указывайте ник и рядом номер версии. Подавать заявки можно несколько раз (разные версии). Результаты голосования будут доступны 1 сентября.
Победитель получает общественное признание и оценку "супер" всех участников конкурса.
PS
Прошу заметить, что это ни какой-нибудь бессмысленный этюд. Эта задача максимально приближена к реальной жизненной потребности.
Здравствуйте, 0K, Вы писали:
0K>В общем, подискутировав немного на тему Exception'ов -- мое любопытство к теме только увеличилось. К сожалению темы быстро перерождаются во флейм, участники быстро перестают слушать друг-друга.
0K>По этому я решил подойти к вопросу с практической точки зрения: поменьше слов, побольше кода.
0K>Прошу заметить, что это ни какой-нибудь бессмысленный этюд. Эта задача максимально приближена к реальной жизненной потребности.
Здравствуйте, GarryIV, Вы писали:
GIV>Во, блин, тебе неймется
Кто не согласен -- аргументируйте!
Создал такую практическую тему, т.к. когда говорили абстрактно -- каждый говорил на своей волне.
Тема нужная. На самом деле если все-таки найдутся смельчаки и приведут практические примеры (я лично планирую поучаствовать) -- вы увидите как раздуется этот простейший код (и насколько он на самом деле сложен).
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, GarryIV, Вы писали:
GIV>>Во, блин, тебе неймется
0K>Кто не согласен -- аргументируйте!
0K>Создал такую практическую тему, т.к. когда говорили абстрактно -- каждый говорил на своей волне.
0K>Тема нужная. На самом деле если все-таки найдутся смельчаки и приведут практические примеры (я лично планирую поучаствовать) -- вы увидите как раздуется этот простейший код (и насколько он на самом деле сложен).
... и бесполезн.
1)Ты не указал юзкейсы для этого кода, а значит его можно бесконечно усложнять всякими частными случаями
2)Для такой задачи работа с файлами — неверный уровень абстракции. Подробнее тут
Здравствуйте, 0K, Вы писали:
0K>После запуска программы пользователь должен ввести название счетчика. Программа должна получить текущий номер из файла счетчика, отобразить его пользователю и записать в файл инкремент. Оставить в виде консольного приложения для упрощения и наглядности. 0K>Программа должна быть не демо, а реальной.
доп. вопросы:
1. что ожидается в случае ошибок? повторный ввод, выход из программы с кодом ошибки и т.п.
2. как должен задаваться уровень детализации ошибок? через командную строку, через консоль, через конфиг?
я б в такой программке вообще бы ничего не ловил — пусть себе фигачит портянку с исключением в консоль.
Здравствуйте, gandjustas, Вы писали:
G>1)Ты не указал юзкейсы для этого кода, а значит его можно бесконечно усложнять всякими частными случаями
И много здесь частных случаев? Приведите несколько примеров, дабы стало понятно, попробую уточнить.
G>2)Для такой задачи работа с файлами — неверный уровень абстракции. Подробнее тут
Никто не мешает вам абстрагироваться от файлов. При запуске программы пользователь вводит не имя файла, а имя счетчика. Оно может не совпадать с именем файла.
Здравствуйте, Neco, Вы писали:
N>доп. вопросы: N>1. что ожидается в случае ошибок? повторный ввод, выход из программы с кодом ошибки и т.п.
Ожидается максимальное удобно для среднестатистического пользователя. Вот ключ. А то попривыкли мозги отключать. А как удобнее -- думайте сами. Конечно, удобнее не закрывать программу а дать возможность ввести правильные данные.
Не забывайте, хороший код можно использовать повторно (к примеру в Win-приложении).
N>2. как должен задаваться уровень детализации ошибок? через командную строку, через консоль, через конфиг?
Это не принципиально. Для простоты можно выводить 2 строчки. В Win-приложении детали можно было бы скрывать и отображать по нажатию на кнопку. В консоли можно просто 2 строчки: одна простая, другая с техническими деталями.
N>я б в такой программке вообще бы ничего не ловил — пусть себе фигачит портянку с исключением в консоль.
1. Ее будут использовать и совсем неопытные пользователи?
2. Нужен код, который легко перенести в Win/WPF-приложение.
0K>>Тема нужная. На самом деле если все-таки найдутся смельчаки и приведут практические примеры (я лично планирую поучаствовать) -- вы увидите как раздуется этот простейший код (и насколько он на самом деле сложен). G>... и бесполезн. G>1)Ты не указал юзкейсы для этого кода, а значит его можно бесконечно усложнять всякими частными случаями G>2)Для такой задачи работа с файлами — неверный уровень абстракции. Подробнее тут
KISS
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, gandjustas, Вы писали:
G>>1)Ты не указал юзкейсы для этого кода, а значит его можно бесконечно усложнять всякими частными случаями
0K>И много здесь частных случаев? Приведите несколько примеров, дабы стало понятно, попробую уточнить.
1)Нету файла
2)Нету каталога
3)Неверные символы в пути
4)Нету доступа на чтение к файлу
5)Нету доступа на запись к файлу
6)Нету доступа на запись в каталог при создании файла
7)Формат пути при вводе (тысячи их)
8)В файле записано не число
9)Целочисленное переполнение
Это далеко не полный перечень.
G>>2)Для такой задачи работа с файлами — неверный уровень абстракции. Подробнее тут
0K>Никто не мешает вам абстрагироваться от файлов. При запуске программы пользователь вводит не имя файла, а имя счетчика. Оно может не совпадать с именем файла.
Прекрасно, тогда я буду использовать Settings и код станет до смешного простым.
N>>я б в такой программке вообще бы ничего не ловил — пусть себе фигачит портянку с исключением в консоль. 0K>1. Ее будут использовать и совсем неопытные пользователи? 0K>2. Нужен код, который легко перенести в Win/WPF-приложение.
Я так чувствую, что сообщению к 100 требования вырисуются, а пока можно даже не читать
Здравствуйте, gandjustas, Вы писали:
G>>>1)Ты не указал юзкейсы для этого кода, а значит его можно бесконечно усложнять всякими частными случаями
0K>>И много здесь частных случаев? Приведите несколько примеров, дабы стало понятно, попробую уточнить.
Понятно. Вы кодер, который реальные задачи не решает. Вот если все по полочкам распишут, разжуют -- тогда смогу накодить чего-нибудь. Здесь задачка чуть сложнее: не только кодинг но и подумать нужно. Вероятно такие решает ваш босс.
Воспринимайте это как реальную жизненную задачу. Пользователь ввел имя счетчика, получил значение. В следующий раз, Когда он введет имя счетчика значение будет на 1 больше. Вот и все. Как вы реализуете детали -- ваше дело. Ключ -- для разных групп пользователей программа должна быть удобна.
G>1)Нету файла G>2)Нету каталога
Там написано: нужно создать.
G>3)Неверные символы в пути
Каком пути? Пользователь вводит не путь а имя счетчика. Имя счетчика может быть любым (или можно ограничить -- как вам удобнее).
G>4)Нету доступа на чтение к файлу G>5)Нету доступа на запись к файлу G>6)Нету доступа на запись в каталог при создании файла G>7)Формат пути при вводе (тысячи их)
Там все указано. Эти ошибки нужно обработать. Как -- ваше дело. Ключ -- удобно для пользователя (для двух групп пользователей: продвинутых и не очень).
G>8)В файле записано не число G>9)Целочисленное переполнение
Думайте о пользователе. Кто об этом должен думать?
G>Это далеко не полный перечень.
Обо всех этих деталях думайте сами. А самую удобную программу мы выберем.
0K>>Никто не мешает вам абстрагироваться от файлов. При запуске программы пользователь вводит не имя файла, а имя счетчика. Оно может не совпадать с именем файла. G>Прекрасно, тогда я буду использовать Settings и код станет до смешного простым.
Не для этой задачи (демонстрация работы с исключениями). Попробуйте написать самостоятельно.
Здравствуйте, 0K, Вы писали:
0K>По этому я решил подойти к вопросу с практической точки зрения: поменьше слов, побольше кода.
0K>И придумал вот такой максимально простой тест
...месье грозился "побольше кода", а в последнюю минуту струсил и поскипал весь код, оставив одни комментарии (левые), причем сам код к исключениям никакого отношения не имеет, потому как по хорошему 99% уйдет на тупую обработку ошибок и только 1% на механизмы сигнализации о том, что случился косяк, что нивилирует всю разницу как именно кричать о том, что у нас что-то не так.
да и потом, что может решить такой тест, даже если он корректный? допустим, механизм А делает код более наглядным, но создает прооблемы переносимости. Допустим, механизм Б делает код понятным и менее захламленным второстепенными деталаями обработки ошибок, которые вынесены совсем в другое пространство. хорошо ли это? да, хорошо, поскольку легко въехать в суть программы. нет, плохо, потому как если нужно проверить насколько корректно выполнена обработка ошибок, тут черт ногу сломит...
давайте все-таки согласимся, что механизм исключений, решая одни проблемы, порождает другие. серебрянной пули нет, увы.
americans fought a war for a freedom. another one to end slavery. so, what do some of them choose to do with their freedom? become slaves.
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, gandjustas, Вы писали:
G>>>>1)Ты не указал юзкейсы для этого кода, а значит его можно бесконечно усложнять всякими частными случаями
0K>>>И много здесь частных случаев? Приведите несколько примеров, дабы стало понятно, попробую уточнить.
0K>Понятно. Вы кодер, который реальные задачи не решает. Вот если все по полочкам распишут, разжуют -- тогда смогу накодить чего-нибудь. Здесь задачка чуть сложнее: не только кодинг но и подумать нужно. Вероятно такие решает ваш босс.
Да не, я вообще-то ИП и у меня несколько работников
0K>Воспринимайте это как реальную жизненную задачу. Пользователь ввел имя счетчика, получил значение. В следующий раз, Когда он введет имя счетчика значение будет на 1 больше. Вот и все. Как вы реализуете детали -- ваше дело. Ключ -- для разных групп пользователей программа должна быть удобна.
G>>1)Нету файла G>>2)Нету каталога
0K>Там написано: нужно создать.
G>>3)Неверные символы в пути
0K>Каком пути? Пользователь вводит не путь а имя счетчика. Имя счетчика может быть любым (или можно ограничить -- как вам удобнее).
G>>4)Нету доступа на чтение к файлу G>>5)Нету доступа на запись к файлу G>>6)Нету доступа на запись в каталог при создании файла G>>7)Формат пути при вводе (тысячи их)
0K>Там все указано. Эти ошибки нужно обработать. Как -- ваше дело. Ключ -- удобно для пользователя (для двух групп пользователей: продвинутых и не очень).
G>>8)В файле записано не число G>>9)Целочисленное переполнение
0K>Думайте о пользователе. Кто об этом должен думать?
G>>Это далеко не полный перечень.
0K>Обо всех этих деталях думайте сами. А самую удобную программу мы выберем.
Так ты тогда приведи юзкейсы, а не код. Код тут кто угодно быстро нарисует.
0K>>>Никто не мешает вам абстрагироваться от файлов. При запуске программы пользователь вводит не имя файла, а имя счетчика. Оно может не совпадать с именем файла. G>>Прекрасно, тогда я буду использовать Settings и код станет до смешного простым.
0K>Не для этой задачи (демонстрация работы с исключениями). Попробуйте написать самостоятельно.
Ну вот весь разговор и слился. Ты привел неадекватный код и пытаешься заставить других с этим кодом работать.
Код твой неадекватен просто потому что нету задач для которых он используется. То есть его можно банально не писать.
Если же взять простую задачу: "при каждом запуске программы выводить текущее значение счетчика и увеличивать его на единицу, значение счетчика по-умолчанию принять за 0", то Settings как раз самый адекватный инструмент.
Здравствуйте, мыщъх, Вы писали:
М>давайте все-таки согласимся, что механизм исключений, решая одни проблемы, порождает другие. серебрянной пули нет, увы.
Вы же вроде бы на C пишите? Там вообще нет исключений -- только коды возврата. Так что о чем речь
Здравствуйте, gandjustas, Вы писали:
G>Код твой неадекватен просто потому что нету задач для которых он используется. То есть его можно банально не писать.
Понятно, что кое-что пришлось упростить для наглядности. Установить некие рамки: использовать именно работу с файлами и прямую запись данных в файл. Можно усложнить так, чтобы Settings было использовать не выгодно -- но потеряется наглядность.
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, GarryIV, Вы писали:
GIV>>Во, блин, тебе неймется
0K>Кто не согласен -- аргументируйте!
0K>Создал такую практическую тему, т.к. когда говорили абстрактно -- каждый говорил на своей волне.
0K>Тема нужная. На самом деле если все-таки найдутся смельчаки и приведут практические примеры (я лично планирую поучаствовать) -- вы увидите как раздуется этот простейший код (и насколько он на самом деле сложен).
Вот как у спецов.
Файл вполне доступен, только видимо слишком большой для программы. Ничего не "разжёвывается".
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, gandjustas, Вы писали:
G>>Код твой неадекватен просто потому что нету задач для которых он используется. То есть его можно банально не писать.
0K>Понятно, что кое-что пришлось упростить для наглядности.
Ты все равно ничего не сказал о задачах, решаемых кодом.
0K>Установить некие рамки: использовать именно работу с файлами и прямую запись данных в файл.
А это как раз детали реализации, мешающие другим писать.
0K>Можно усложнить так, чтобы Settings было использовать не выгодно -- но потеряется наглядность.
Да не надо усложнять, просто приведи описание задачи, а не код, который её якобы решает. Код тебе тут напишут моментально.
Здравствуйте, LF, Вы писали:
LF>От нечего делать написал, пинайте
1)WriteToFile не выкидывает эксепшн в случае неудачи
2)Не проверяется ACL, а только разрешения CAS
3)Не обрабатываются абсолютные пути
4)Не обрабатывается отсутствие каталога
И еще много всего.
ЗЫ. Не тот уровень абстракции, чтобы такие задачи решать.
Здравствуйте, gandjustas, Вы писали:
0K>>Можно усложнить так, чтобы Settings было использовать не выгодно -- но потеряется наглядность. G>Да не надо усложнять, просто приведи описание задачи, а не код, который её якобы решает. Код тебе тут напишут моментально.
Задача: продемонстрировать правильную работу с исключениями на примере простой программы, которая напрямую читает файл с числом, изменяет его и записывает обратно (см выше). Представьте что нет готовых классов для работы с пользовательскими настройками -- делайте прямым обращением к файлу. Иначе всегда можно найти готовую библиотеку. Почему стоит себя ограничивать именно библиотеками MS?
Здравствуйте, 0K, Вы писали:
0K>Ожидается максимальное удобно для среднестатистического пользователя. Вот ключ. А то попривыкли мозги отключать.
Что-то вы какой-то злой сёдня.
Среднестатический пользователь бывает разный. Есь пользователь-домохозяйка, пользователь-айтишник, пользователь-прогер (т.е. пользователь кода). Удобство у каждого своё.
Подход "думать за пользователя" он вообще неправильный. Думать имеет смысл, когда нет возможности спросить его или он сам не знает как точно надо — тогда и надо вводить все эти гибкости в коде, которые усложнят его создание, но упростят изменения.
А когда заказчик под боком и вопросы на поверхности — надо спрашивать и получать ответы.
Но понятно, что вам лень думать даже над простыми сценариями, так что я буду писать самый короткий и понятный код, взяв все свои предположения о требованиях за истину. И этот код после тестирования будем усложнять, лады?
Пока что вижу такие основные требования:
1. программа должна накидывать счётчик, показывая старое значение (почему-то) и записывая новое;
2. программа должна уметь выводить два типа сообщений;
3. программа после ошибки должна позволять вводить данные повторно;
заметьте, нет примеров ошибок и сообщений которые хотел бы получать пользователь. поэтому я их тоже сам додумаю.
0K>1. Ее будут использовать и совсем неопытные пользователи?
выше были среднестатические ))
0K>2. Нужен код, который легко перенести в Win/WPF-приложение.
код бизнес-логики или код вывода ошибок? вывод ошибок непереносим — он везде уникален. Точнее может он и переносим (можно и зайца научить курить), но смысл?
В общем, вот мой код на конкурс самых суперских программистов.
using System;
using System.IO;
using System.Reflection;
namespace ConsoleApplication1 {
#region Presentation
class Program {
static void Main(string[] args) {
bool exitProgram = false;
do {
try {
Console.WriteLine("Enter the name of a counter and press ENTER:");
string fileName = Console.ReadLine();
var incrementor = new Incrementor((new FileNameGenerator()).GenerateFileName(fileName));
var result = incrementor.PerformIncrement();
Console.WriteLine("Value of counter: {0}", result.PreviousValue);
exitProgram = true;
Console.WriteLine("Press enter for exit.");
Console.ReadLine();
} catch (Exception ex) {
var myFriend = new FriendlyMessageGenerator();
var msg = myFriend.GetFriendly(ex);
Console.WriteLine("Friendly message: {0}", msg.Message);
Console.WriteLine("Error details: {0}", msg.Details);
Console.WriteLine("Would you like to try again?");
if (Console.ReadLine().Equals("n", StringComparison.OrdinalIgnoreCase)) {
exitProgram = true;
}
}
} while (!exitProgram);
}
}
#endregion
#region SharedApplicationComponent
public class FileNameGenerator {
private const string EXTENSION = ".txt";
public string GenerateFileName(string counterName) {
return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + Path.DirectorySeparatorChar + counterName + EXTENSION;
}
}
public class FriendlyMessageGenerator {
public FriendlyMessage GetFriendly(Exception ex) {
string message;
if (ex is IncrementorException) {
message = (ex as IncrementorException).Message;
} else {
message = "Unexpected error has been happened during executing the task. Please, see the details.";
}
return new FriendlyMessage(message, ex);
}
}
public class FriendlyMessage {
public FriendlyMessage(string message, Exception exception) {
Message = message;
Details = exception;
}
public string Message { get; private set; }
public Exception Details { get; private set; }
}
#endregion
#region BusinessLogicComponent
public class Incrementor {
private string _counterFileName;
private const int STARTUP_NUMBER = 0;
public Incrementor(string counterFileName) {
_counterFileName = counterFileName;
}
public Result PerformIncrement() {
CreateFileIfItDoesNotExist();
var oldValue = ReadCurrentValue();
var newValue = oldValue + 1;
WriteNewValue(newValue);
return new Result(oldValue, newValue);
}
private int ReadCurrentValue() {
try {
return int.Parse(File.ReadAllText(_counterFileName));
} catch (Exception ex) {
throw new IncrementorException("Error during reading current value", ex);
}
}
private void WriteNewValue(int newValue) {
try {
File.WriteAllText(_counterFileName, newValue.ToString());
} catch (Exception ex) {
throw new IncrementorException("Error during writing new value", ex);
}
}
private void CreateFileIfItDoesNotExist() {
if (!File.Exists(_counterFileName)) {
try {
File.Create(_counterFileName).Close();
File.WriteAllText(_counterFileName, STARTUP_NUMBER.ToString());
} catch (Exception ex) {
throw new IncrementorException("Error during file creating", ex);
}
}
}
}
public class IncrementorException : Exception {
public IncrementorException() { }
public IncrementorException(string message) : base(message) { }
public IncrementorException(string message, Exception inner) : base(message, inner) { }
protected IncrementorException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context)
: base(info, context) { }
}
public class Result {
public Result(int previousValue, int newValue) {
PreviousValue = previousValue;
NewValue = newValue;
}
public int PreviousValue { get; private set; }
public int NewValue { get; private set; }
}
#endregion
}
т.е. если в двух словах, то я за такой вид обработки ошибок:
private void WriteNewValue(int newValue) {
try {
File.WriteAllText(_counterFileName, newValue.ToString());
} catch (Exception ex) {
throw new IncrementorException("Error during writing new value", ex);
}
}
это должно делаться на более-менее высоком уровне, чтобы не мельчить.
И реальный FriendlyMessageGenerator навороченней — он делает рекурсивный обход всех Inner сообщений и собирает информацию не только из моих собственных исключений (у которых надо просто Message прочитать), но и читает Message'ы оракловых исключений и позволяет выдавать дружественные ошибки типа "База данных недоступна" или "Нарушение целостности связки Работник-Отдел.". Это если сильно надо. А в большинстве случаев просто смотришь в детали и понимаешь в чём дело.
Здравствуйте, microcod, Вы писали:
M>Файл вполне доступен, только видимо слишком большой для программы. Ничего не "разжёвывается".
Плохой пример.
Вот PaintNet что выдал (стандартный диалог, кстати):
---------------------------
Open
---------------------------
File C:\#$# does not exist.
Verify that the correct file name was given.
---------------------------
OK
---------------------------
---------------------------
Open
---------------------------
8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
The above file name is invalid.
---------------------------
OK
---------------------------
А вот если файл заблокирован:
---------------------------
Paint.NET
---------------------------
An I/O error occurred when writing to the file.
---------------------------
OK
---------------------------
Я так понимаю это просто Message от IOException.
---------------------------
Paint.NET
---------------------------
There was an error reading the file from the media.
---------------------------
OK
---------------------------
А это просто Paint:
---------------------------
Paint
---------------------------
A sharing violation occurred while accessing C:\Untitled.png.
---------------------------
OK
---------------------------
Здравствуйте, gandjustas, Вы писали:
G>1)WriteToFile не выкидывает эксепшн в случае неудачи G>2)Не проверяется ACL, а только разрешения CAS G>3)Не обрабатываются абсолютные пути G>4)Не обрабатывается отсутствие каталога
Попробуйте не повторить его ошибок. Критиковать каждый может.
G>ЗЫ. Не тот уровень абстракции, чтобы такие задачи решать.
Т.е. вы хотите сказать, что простым чтением из файла и простой записью в файл эта задача не разрешима в принципе? Или вы просто не в состоянии этого сделать?
Здравствуйте, gandjustas, Вы писали:
0K>>Можно усложнить так, чтобы Settings было использовать не выгодно -- но потеряется наглядность. G>Да не надо усложнять, просто приведи описание задачи, а не код, который её якобы решает. Код тебе тут напишут моментально.
Кстати, придумал как вам объяснить.
Нужно чтобы код писал именно в файл (простой файл, внутри цифра), т.к. этот файл будут использовать из другой программы, которая не умеет работать с XML (и перекомпилить эту супер-мега программу нельзя, т.к. нет исходного кода и толком никто не знает как именно она работает -- единственный интерфейс -- этот файл со счетчиком).
Понятна теперь задача? Понятно почему нельзя Settings использовать?
А теперь покажите на практике, что вы действительно такой умный. Предидущие два ответа были с глобальным перехватом Exception. Вы против. Покажите нам как эту задачу решить правильно Или вы только на словах такой умный?
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, gandjustas, Вы писали:
0K>>>Можно усложнить так, чтобы Settings было использовать не выгодно -- но потеряется наглядность. G>>Да не надо усложнять, просто приведи описание задачи, а не код, который её якобы решает. Код тебе тут напишут моментально.
0K>Задача: продемонстрировать правильную работу с исключениями на примере простой программы, которая напрямую читает файл с числом, изменяет его и записывает обратно (см выше).
Переведу на русский: продемонстрировать правильную работу с исключениями на примере неправильной программы.
Вообще-то я говорил про задачу с точки зрения пользователя.
0K>Представьте что нет готовых классов для работы с пользовательскими настройками -- делайте прямым обращением к файлу. Иначе всегда можно найти готовую библиотеку. Почему стоит себя ограничивать именно библиотеками MS?
1)Они идут в комплекте
2)Они решают задачи
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, gandjustas, Вы писали:
G>>1)WriteToFile не выкидывает эксепшн в случае неудачи G>>2)Не проверяется ACL, а только разрешения CAS G>>3)Не обрабатываются абсолютные пути G>>4)Не обрабатывается отсутствие каталога
0K>Попробуйте не повторить его ошибок. Критиковать каждый может.
не повторять ошибок — сделать на более высоком уровне абстракции. Обычный механизм сеттингов защитит от обработки отсутствия файла, проблем с доступом, именами файлов и неправильными значениями. Остальные ошибки и нет смысла обрабатывать ибо сделать все равно ничего не получится.
G>>ЗЫ. Не тот уровень абстракции, чтобы такие задачи решать.
0K>Т.е. вы хотите сказать, что простым чтением из файла и простой записью в файл эта задача не разрешима в принципе? Или вы просто не в состоянии этого сделать?
Вполне в состоянии, но полезный код погрязнет в обилии низкоуровневых, и вообще-то ненужных, деталей.
Единственный способ правильно решать такую задачу, отчасти повторить механизм сеттингов и использовать его в своем коде. Но этим сильно лень заниматься ибо есть стандартные средства.
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, gandjustas, Вы писали:
0K>>>Можно усложнить так, чтобы Settings было использовать не выгодно -- но потеряется наглядность. G>>Да не надо усложнять, просто приведи описание задачи, а не код, который её якобы решает. Код тебе тут напишут моментально.
0K>Кстати, придумал как вам объяснить.
0K>Нужно чтобы код писал именно в файл (простой файл, внутри цифра), т.к. этот файл будут использовать из другой программы, которая не умеет работать с XML (и перекомпилить эту супер-мега программу нельзя, т.к. нет исходного кода и толком никто не знает как именно она работает -- единственный интерфейс -- этот файл со счетчиком).
То есть
1)Файл гарантировано есть и заранее известен
2)Доступ к файлу гарантировано есть
3)Значение счетчика, записанного туда, обязательно корректно
0K>Понятна теперь задача? Понятно почему нельзя Settings использовать?
Конечно
0K>А теперь покажите на практике, что вы действительно такой умный.
Возьму код из первого поста и пропишу в нем путь к файлу.
0K>Предидущие два ответа были с глобальным перехватом Exception. Вы против.
Я не против.
0K>Покажите нам как эту задачу решить правильно
См выше.
Здравствуйте, 0K, Вы писали:
0K>Понятно. Вы кодер, который реальные задачи не решает. Вот если все по полочкам распишут, разжуют -- тогда смогу накодить чего-нибудь. Здесь задачка чуть сложнее: не только кодинг но и подумать нужно. Вероятно такие решает ваш босс.
Слудующий переход на личности или неуважительное отношение к посетителям форума закончится продолжительным баном.
Если нам не помогут, то мы тоже никого не пощадим.
0K>А это просто Paint:
0K>--------------------------- 0K>Paint 0K>--------------------------- 0K>A sharing violation occurred while accessing C:\Untitled.png. 0K>--------------------------- 0K>OK 0K>---------------------------
0K>Здесь даже детали есть.
Здесь правильно про сценарии использования говорили. У нас половина такого кода просто прокидывает исходное исключение дальше и выводит юзеру окно с требованием позвать администратора системы. В окне все аж со стектрейсом. Ибо не юзерское это дело — голову ломать над тем — каких прав на чтение какого файла почему не хватает и как это ему, юзеру, исправлять.
Здравствуйте, 0K, Вы писали:
0K>И придумал вот такой максимально простой тест (буквально 7 строчек кода внутри функции) для выявления человека, который действительно лучше всех умеет работать с исключениями. Не на словах а на деле. Вот сам код:
В отличие от темы "курицы и яйца" код однозначно вторичен, а первично описание или ТЗ или use case. Короче, любимое наше "что делать". (А потом, само собой, "кто виноват".)
А поскольку описания нет, а есть только код, значит, не будем фантазировать, а будем его читать. Я придерживаюсь мнения тех, кто считает, что код пишут, чтобы его читать.
Пронумеруем код для удобства анализа, только значащие строки.
0K>
0K>using System;
0K>using System.IO;
0K>namespace ConsoleApplication1
0K>{
0K> class Program
0K> {
0K> static void Main(string[] args)
0K> {
0K>01 Console.WriteLine("Введите имя счетчика и нажмите ENTER:");
0K>02 string fileName = Console.ReadLine();
0K> // Если файла не существует -- нужно создать.
0K> // Может не быть доступа к файлу
0K> // Имя счетчика может быть некорректным -- с этим что-то нужно сделать
0K>03 string text = File.ReadAllText(fileName);
0K>04 int count = int.Parse(text);
0K>05 Console.WriteLine("Значение счетчика: ", count);
0K>06 count++;
0K> // Может быть запрещена запись в файл
0K>07 File.WriteAllText(fileName, count.ToString());
0K> }
0K> }
0K>}
0K>
Что мы видим? В строке 01 вывод приглашения, если мы его вывести не сможем, то продолжать работу дальше бессмысленно. В строке 02 мы определяем переменную и пробуем ввести нечто, являющееся именем файла. Опять же, продолжение бессмысленно, если вылетит исключение. В строке 03 мы видим чтение файла, а перед этим три строчки комментария, которые и будут нашим руководством к действию. Если файла не существует, нам приедет FileNotFoundException, что приедет в случае отсутствия доступа или некорректного для FS имени — с лёта не скажу, а искать влом. Тем не менее, несколько минут отладки в среде и эмуляция проблем, либо запуск приложения в реальных пользовательских условиях с протоколируованием необработанных исключений — замечательно поймают названия исключений, на которые мы и будем довешивать свою реакцию, если она нужна и разумна. По аналогии с NotFoundException. Ещё есть вариант почитать описание вызываемых методов, там иногда бывает нужная нам информация об исключениях. Не всегда, конечно, документация поспевает за полётом мысли, но всё же. Я намеренно не стал читать, и реагировать на. Начинаем писать код:
Console.WriteLine("Введите имя счетчика и нажмите ENTER:");
string fileName = Console.ReadLine();
string text = "";
try
{
text = File.ReadAllText(fileName);
}
catch (NotFoundException e)
{
text = "-1"
}
Что мы имеем? Определение переменной text вынесено за блок try, реакция на NotFoundException одна — подавляем, ибо отсутствие файла не является основанием для аварийного завершения работы, плюс записываем в переменную text значение "-1".
Читаем дальше. В строке 04 видим разбор полученного на предыдущем шаге значения. Тут мы можем словить FormatException, если окажется, что то, что мы проситали — не число. Реагируем на из предположения (тут включилась Боевая Телепатия, в коде про это ничего нет!), что если там чёртичто, то должен быть сбоку бантик, т.е. "-1":
Опять же, определение переменной count уехало за блок try.
В строке 05 выводим значение, если не смогли, то опять всё бессмысленно. Аналогично и для строки 06. А вот со строкой 07 надо предусмотреть реакцию на SecurityException (тут опять лезет Боевая Телепатия и шепчет про UnauthorizedAccessException, но я её убедительно гоню указующим жестом.). Но какую реакцию? Читаемый нами код в строке 07 и комментарий над ней ничего не говорит нам о том, как же мы должны реагировать. Раз не говорит, то и реакции не будет. Тут Боевая Телепатия в третий раз закинула невод, и предлагает регировать выводом сообщения в консоль, а то мало перехватов исключений. И по аналогии добавить вывод в консоль сообщения об отсутствующем/существующем файле. Поскольку целью программы является инкремент счётчика, и именно этого мы сделать не можем из-за прилетевшего исключения, то мы не подавляем, а пробрасываем исключение в вызывающий код. Получаем:
static void Main(string[] args)
{
Console.WriteLine("Введите имя счетчика и нажмите ENTER:");
string fileName = Console.ReadLine();
string text = "";
try
{
text = File.ReadAllText(fileName);
Console.WriteLine("Счётчик '{0}' найден.", text);
}
catch (NotFoundException e)
{
text = "-1"Console.WriteLine("Счётчик '{0}' не найден и будет создан.", text);
}
int count = 0;
try
{
count = int.Parse(text);
}
catch (FormatException e)
{
count = -1;
}
Console.WriteLine("Значение счетчика: ", count);
count++;
try
{
File.WriteAllText(fileName, count.ToString());
}
catch (SecurityException e)
{
Console.WriteLine("Счётчик '{0}' обновить не удалось.", text);throw;
}
}
Какие будут выводы?
0. Консольные приложения пишутся не так, как оконные. Без интерактива. Обычно они представляют собой команды, принимают параметры из командной строки, понимают перенаправление вывода, взводят errorlevel, и пр. Автором темы это всё, похоже, опущено.
1. Код, выделенный жирным, и особенно последний блок try-catch надо выкинуть, ибо написан он под воздействием Боевой Телепатии, а таковую надо гнать.
2. Предложенное автором темы описание задачи не достаточное и не полное, что требует ментальных усилий по удержанию Боевой Телепатии в узде от желания врубить воображение и безосновательно раздуть код.
3. Поскольку в описании автора темы не предусмотрено реакции программы на ошибки и сбои, кроме скудных комментариев в двух местах, то и я не буду выдумывать отсебятину, глобальные перехваты и вывод красочных сообщений. Итого код:
static void Main(string[] args)
{
Console.WriteLine("Введите имя счетчика и нажмите ENTER:");
string fileName = Console.ReadLine();
string text = "";
try
{
text = File.ReadAllText(fileName);
}
catch (NotFoundException e)
{
text = "-1"
}
int count = 0;
try
{
count = int.Parse(text);
}
catch (FormatException e)
{
count = -1;
}
Console.WriteLine("Значение счетчика: ", count);
count++;
File.WriteAllText(fileName, count.ToString());
}
Здравствуйте, gandjustas, Вы писали:
G>То есть G>1)Файл гарантировано есть и заранее известен
Нет. Если файл не существует -- программа должна его создать.
G>2)Доступ к файлу гарантировано есть
Его уже может не быть по той причине, что файл используется второй программой.
G>3)Значение счетчика, записанного туда, обязательно корректно
Я же сказал: этот файл использует другая программа, которую мы не можем изменить. Она работает криво, может чего-то не того записать в файл.
G>Возьму код из первого поста и пропишу в нем путь к файлу.
См. то что вы забыли учесть выше.
0K>>Покажите нам как эту задачу решить правильно
Давайте пишите код. Неужели вам не лень базикать и лень писать код? Слова -- ничто, код -- все.
Здравствуйте, Nikolay_P_I, Вы писали:
N_P>Здесь правильно про сценарии использования говорили. У нас половина такого кода просто прокидывает исходное исключение дальше и выводит юзеру окно с требованием позвать администратора системы. В окне все аж со стектрейсом. Ибо не юзерское это дело — голову ломать над тем — каких прав на чтение какого файла почему не хватает и как это ему, юзеру, исправлять.
А админ как узнает в чем проблема? 2 сообщения нужна: для админа и для пользователя. Там де написано в самом первом посте.
Здравствуйте, LF, Вы писали:
LF>От нечего делать написал, пинайте
Во-первых, благодарю за желание принять участие в конкурсе
Теперь критика.
По существу:
1. Если файл занят другим процессом, возникает ошибка: "Упс, не предвиденная ошибка: Could not find file..." Информация в ней не соответствует действительности, т.к. файл существует.
2. Если имя файла ввести с '\', то опять некорректная ошибка: "Упс, не предвиденная ошибка: Could not find a part of the path 'c:\gr\g'.. Обратитесь в службу поддержки."
3. С делегатами вы перемудрили -- программа выглядет запутанной. Можно было бы как-то проще ее написать. В Win-приложение со стандартными errorProvider переносить будет очень сложно...
Дополнительно:
1. После проверки прав на чтение/запись но перед самим актом чтения/записи, могли произойти изменения (хоть и маловероятно). Будет выведена непонятная ошибка
2. Warning: Review the following for a possible security vulnerability: Parameter 'fileIoPermissionAccess' of 'Worker.CheckAccessForFile(string, FileIOPermissionAccess)' is being passed to a 'FileIOPermission' constructor. Кстати, кто объяснит почему предупреждение?
Здравствуйте, Neco, Вы писали:
N>это должно делаться на более-менее высоком уровне, чтобы не мельчить.
Не на таком уж низком уровне работа требуется Никто к командам ассемблера и устройству файлововой системы не прибегает.
Теперь критика.
По существу:
1. У вас три перехвата базового Exception. Вы, вероятно, читали в MSDN что так делать нельзя. Но проигнорировали, т.к. не нашли вы этом большого смысла. Смысл есть -- вы нейтрализуете (пытаетесь нейтрализовать) низкоуровневые исключения, такие как ExecutionEngineException, OutOfMemoryException.
Но благодаря такой стратегии ваш код -- неубиваемый. Мне не удалось найти ни одного способа заставить его упасть или сделать так, чтобы возникло нестандартное сообщение об ошибке.
Но опять же. Вы нарушили требования MS не перехватывать базовый Exception и в некоторых случаях ваш код все-таки сработает некорректно. Кроме того, в системах автоматической сборки его просто не пропустит FxCop.
Дополнительно:
1. int.ToString/int.Parse -- нужно указать нейтральную культуру.
2. Класс Exception не помечен аттрибутом Serializable.
N>Кстати, в ответ попрошу и свой код выложить.
Во первых, отвечу на вопросы.
A>Какие будут выводы? A>0. Консольные приложения пишутся не так, как оконные. Без интерактива. Обычно они представляют собой команды, принимают параметры из командной строки, понимают перенаправление вывода, взводят errorlevel, и пр. Автором темы это всё, похоже, опущено.
Я же даже подсказал, код лучше всего делать универсальным: вынести логику в отдельный класс для повторного использования.
A>1. Код, выделенный жирным, и особенно последний блок try-catch надо выкинуть, ибо написан он под воздействием Боевой Телепатии, а таковую надо гнать. A>2. Предложенное автором темы описание задачи не достаточное и не полное, что требует ментальных усилий по удержанию Боевой Телепатии в узде от желания врубить воображение и безосновательно раздуть код. A>3. Поскольку в описании автора темы не предусмотрено реакции программы на ошибки и сбои, кроме скудных комментариев в двух местах, то и я не буду выдумывать отсебятину, глобальные перехваты и вывод красочных сообщений. Итого код:
А это уже сами думайте как пользователю удобнее. В данном задании требуется не только кодирование, но и думание как удобнее сделать для пользователя (т.е. нужны начальные знания в этой области).
Теперь критика.
По существу:
1. Ваша программа абсолютно не предназначена для среднего пользователя, только для админа какого-нибудь. Сообщения об ошиках среднему пользователю будут не понятны. Это ключевой момент, дальше то и обсуждать нечего. Требование было (самое первое сообщение) вывести 2 сообщения для разных уровней пользователей.
2. Код непереносим. Его нельзя задействовать в Win или WPF -приложении.
3. Вы перепутали FileNotFoundException и NotFoundException. Так что код даже не работает.
Дополнительно
1. Если пользователь ошибся при вводе данных -- ему не предоставляется второй шанс. Это не юзабельно. Хотя такого требования не было. Но кто о пользователе думать будет?
Здравствуйте, akasoft, Вы писали:
A>Читаем дальше. В строке 04 видим разбор полученного на предыдущем шаге значения. Тут мы можем словить FormatException, если окажется, что то, что мы проситали — не число. Реагируем на из предположения (тут включилась Боевая Телепатия, в коде про это ничего нет!), что если там чёртичто, то должен быть сбоку бантик, т.е. "-1":
A>
A>int count = 0;
A>try
A>{
A> count = int.Parse(text);
A>}
A>catch (FormatException e)
A>{
A> count = -1;
A>}
A>
Фишка в том, что создатели языка подумали об этой ситуации, и добавили метод TryParse, чтобы не строить логику на исключениях:
int count = 0;
if(!int.TryParse(text, out count))
count = -1;
В других случаях язык тоже предлагает много инструментов для того, чтобы не строить логику на исключениях.
Для баз данных это Transaction Scope, для ресурсов это using + disposable.
Но топикстартер умышленно выбрал работу с файлами — единственное место, где в .NET спорная работа с исключениями. Поэтому конкурс изначально предвзятый.
Здравствуйте, 0K, Вы писали:
0K>1. У вас три перехвата базового Exception. Вы, вероятно, читали в MSDN что так делать нельзя.
Не, не читал. Вот счас прочитал только.
# It hides bugs
не вижу как. предположим у меня где-то там баг. его обернут в exception более точно указывающий местоположение и вытолкнут дальше. баг не скрыт.
# Handling all exceptions without exception-specific handling logic will leave the application in an unknown state.
не согласен. я не отменяю исключения (не гашу их) — просто дополняю информацией.
# It circumvents higher-level exception handling.
это с одной стороны звучит убедительно. но с другой стороны — на фига вышестоящему классу заботиться о моих ошибках? сейчас я работаю с файловой системой, а завтра изменю реализацию и буду работать с сокетами (т.е. опять же с инкрменентной целью, но уже по сети). а послезавтра меня осенит, что инкрементность можно возложить на оракл и просто брать оттуда значение следующего сиквенса. заметьте — функционал класса с внешней точки зрения не поменялся. что же теперь пользователям моего класса писать надстройки на все случаи жизни? это невозможно и не нужно.
Если так уж нужно дать знать вышестоящему классу больше, то лучше делать несколько видов исключений.
IncrementorException
IncrementorReadErrorException
IncrementorWriteErrorException
по одному на каждый приватный метод. но это сильно утяжелит реализацию и я не знаю маньяка, который хотел бы с этим что-то делать при использовании моего класса (и главное, не знаю что бы он мог с этим сделать и зачем это ему).
A higher-level exception handler may know exactly what to do with a certain type of exception; possibly much better than your code. By using catch(Exception) you circumvent that and end up with the above--the application may be in a unknown state.
жалко, что они не приводят здесь же пример. как вышестоящий хэндлер может знать лучше, что делать с ошибкой?
как-то так?
string fileName = Console.ReadLine();
var incrementor = new Incrementor((new FileNameGenerator()).GenerateFileName(fileName));
try {
var result = incrementor.PerformIncrement();
} catch (FileNotFoundException ex) {
Console.WriteLine("The file [{0}] was not found", fileName);
}
ну и с какого перепуга вышестоящий хэндлер может решить, что именно этот файл не найден? может я внутри класса лазаю в какие-то другие файлы и один из них у меня вдруг не нашёлся неожиданно?
и AccessDenied вышестоящий класс не сможет интерпретировать вменяемо — во время чтения не хватило прав или во время записи?
0K>Но благодаря такой стратегии ваш код -- неубиваемый. Мне не удалось найти ни одного способа заставить его упасть или сделать так, чтобы возникло нестандартное сообщение об ошибке. 0K>Но опять же. Вы нарушили требования MS не перехватывать базовый Exception и в некоторых случаях ваш код все-таки сработает некорректно. Кроме того, в системах автоматической сборки его просто не пропустит FxCop.
Требования должны идти с решениями. Иначе это выглядит как "я не знаю как надо, но вы делаете неправильно".
Если бы в дотнете были ограничения на виды исключений, как в Java, то такая рекомендация имела хоть какой-то смысл (да и то — переписывать классы-пользователи по каждому новому чиху подкласса — себе дороже).
про FxCop — согласился бы наверное, если бы пользовался им.
ну а в каких случаях мой код работает некорректно?
кстати, неубивамость — не моя цель. StackOverflow всё равно убьёт приложение. Моя цель — как можно больше рассказать об исключительной ситуации через Exception тому, кто будет иметь с ним дело.
0K>1. int.ToString/int.Parse -- нужно указать нейтральную культуру.
принимается.
0K>2. Класс Exception не помечен аттрибутом Serializable.
принимается.
Кстати, пара слов о рекомендацяих MS — сами-то они очень сильно нарушают общечеловеческие рекомендации по разделению ответственностей, например. Или взять их обработку ошибок в VB6, VBA, VBS — кто это им рекомендовал?
Здравствуйте, 0K, Вы писали:
0K>Нет. Если файл не существует -- программа должна его создать.
Файл будет создан один раз, это можно вынести вне кода, увеличивающего значение счетчика.
G>>3)Значение счетчика, записанного туда, обязательно корректно 0K>Я же сказал: этот файл использует другая программа, которую мы не можем изменить. Она работает криво, может чего-то не того записать в файл.
Ну это уже чушь, если "другая программа" может записать что угодно, то и я могу записать что угодно и не париться по этому поводу.
G>>Возьму код из первого поста и пропишу в нем путь к файлу. 0K>См. то что вы забыли учесть выше.
0K>>>Покажите нам как эту задачу решить правильно 0K>Давайте пишите код. Неужели вам не лень базикать и лень писать код? Слова -- ничто, код -- все.
Мне всегда лень писать код. А если писать, то надо определиться с тем что он делает и не делает.
Здравствуйте, 0K, Вы писали:
N_P>>Здесь правильно про сценарии использования говорили. У нас половина такого кода просто прокидывает исходное исключение дальше и выводит юзеру окно с требованием позвать администратора системы. В окне все аж со стектрейсом. Ибо не юзерское это дело — голову ломать над тем — каких прав на чтение какого файла почему не хватает и как это ему, юзеру, исправлять.
0K>А админ как узнает в чем проблема? 2 сообщения нужна: для админа и для пользователя. Там де написано в самом первом посте.
Потому и полное исключение со стектрейсом. Не поймет админ — позвонит разработчику.
0K>Вы сейчас ищите причину почему не делать.
Читайте внимательнее — "половина такого кода". Остальная, где можно предусмотреть ситуацию — или сама что-то правит незаметно от юзера или нормально с ним разговаривает.
Просто если к старту юзерской программы к ней должен быть нормальный, существующий и корректно разбирающийся конфиг — не дело юзера разбираться почему даже "file not found", не говоря уж о "ошибка при чтении XML, строка 5, позиция 4" потому как ЛЮБОЙ факт ошибки чтения конфига в такой ситуации свидетельствует о создании нештатной ситуации и неизвестно — чего там еще не так.
Здравствуйте, Nikolay_P_I, Вы писали:
0K>>А админ как узнает в чем проблема? 2 сообщения нужна: для админа и для пользователя. Там де написано в самом первом посте.
N_P>Потому и полное исключение со стектрейсом. Не поймет админ — позвонит разработчику.
Ну вот. Программа должна работать так, чтобы разработчик мог отдыхать. А если делать по вашей логики -- то разработчик только и будет "на телефоне сидеть". Это главный принцип хорошей программы: программа работает, разработчик отдыхает.
N_P>Просто если к старту юзерской программы к ней должен быть нормальный, существующий и корректно разбирающийся конфиг — не дело юзера разбираться почему даже "file not found", не говоря уж о "ошибка при чтении XML, строка 5, позиция 4" потому как ЛЮБОЙ факт ошибки чтения конфига в такой ситуации свидетельствует о создании нештатной ситуации и неизвестно — чего там еще не так.
Так и нужно писать: файл поврежден, желаете ли создать новый файл?
Здравствуйте, gandjustas, Вы писали:
G>Здравствуйте, 0K, Вы писали:
0K>>Нет. Если файл не существует -- программа должна его создать. G>Файл будет создан один раз, это можно вынести вне кода, увеличивающего значение счетчика.
Нет, по условию если файла нет -- нужно создать.
G>>>3)Значение счетчика, записанного туда, обязательно корректно 0K>>Я же сказал: этот файл использует другая программа, которую мы не можем изменить. Она работает криво, может чего-то не того записать в файл. G>Ну это уже чушь, если "другая программа" может записать что угодно, то и я могу записать что угодно и не париться по этому поводу.
Опять-таки нет. Чужие ошибки не дают нам права делать плохо.
0K>>>>Покажите нам как эту задачу решить правильно 0K>>Давайте пишите код. Неужели вам не лень базикать и лень писать код? Слова -- ничто, код -- все. G>Мне всегда лень писать код. А если писать, то надо определиться с тем что он делает и не делает.
А комментировать вам не лень? Легче написать несколько строчек, чем читать все это и переливать из пустого в порожнее. Несколько строчек вашего кода могли бы убедить в вашей правоте лучше 100 тыс. слов.
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, gandjustas, Вы писали:
G>>Здравствуйте, 0K, Вы писали:
0K>>>Нет. Если файл не существует -- программа должна его создать. G>>Файл будет создан один раз, это можно вынести вне кода, увеличивающего значение счетчика.
0K>Нет, по условию если файла нет -- нужно создать.
Если файла нет, то читающая программа в принципе не работает.
G>>>>3)Значение счетчика, записанного туда, обязательно корректно 0K>>>Я же сказал: этот файл использует другая программа, которую мы не можем изменить. Она работает криво, может чего-то не того записать в файл. G>>Ну это уже чушь, если "другая программа" может записать что угодно, то и я могу записать что угодно и не париться по этому поводу.
0K>Опять-таки нет. Чужие ошибки не дают нам права делать плохо.
Не в этом вопрос, а в том что при неизвестной логике "другой программы" делать что-то бессмысленно.
0K>>>>>Покажите нам как эту задачу решить правильно 0K>>>Давайте пишите код. Неужели вам не лень базикать и лень писать код? Слова -- ничто, код -- все. G>>Мне всегда лень писать код. А если писать, то надо определиться с тем что он делает и не делает.
0K>А комментировать вам не лень?
Не-а, потому что обсуждение помогает прийти к тому что на самом деле может понадобиться.
0K>Легче написать несколько строчек, чем читать все это и переливать из пустого в порожнее. Несколько строчек вашего кода могли бы убедить в вашей правоте лучше 100 тыс. слов.
Я думаю тебя они бы ни в чем не убедили, потому что тебе надо не работающую программу получить, а что-то кому-то доказать.
Приведи требования (полные и непротиворчеивые в таком масштабе это просто) и будет тебе программа.
Вкратце ситуация выглядит так: ты приводишь код, который решает непонятно какую задачу и просишь других переписать этот код правильно. Но правильность кода зависит исключительно от задач. А задачу ты формулировать не хочешь, потому что тогда исчезнет твоя цель доказывания чего-то кому-то.
Здравствуйте, gandjustas, Вы писали:
G>Вкратце ситуация выглядит так: ты приводишь код, который решает непонятно какую задачу и просишь других переписать этот код правильно. Но правильность кода зависит исключительно от задач. А задачу ты формулировать не хочешь...
Здравствуйте, GarryIV, Вы писали:
GIV>А если файл находится где то в сети надо выдать сообщение "Проверьте настроки сетевого подключения"?
Нет, надо выдать "Желаете ли проверить настойки сетевого подключения?" И ещё стопятьсот вопросов, потому как диагностика проблем всё ещё является искусством.
Здравствуйте, gandjustas, Вы писали:
0K>>Нет, по условию если файла нет -- нужно создать. G>Если файла нет, то читающая программа в принципе не работает.
Так вот чтобы пользователь не создавал его вручную -- позаботьтесь об этом и создайте файл.
0K>>Опять-таки нет. Чужие ошибки не дают нам права делать плохо. G>Не в этом вопрос, а в том что при неизвестной логике "другой программы" делать что-то бессмысленно.
А зачем вам логика это другой программы? Ваша задача считать из файла число и записать его инкремент. Если файла не существует -- создать. Причем сделать максимально удобно для пользователя (если ошибся пользователь -- внятно пояснить и дать возможность повторить ввод). Если файл будет заблокирован/не доступен и пр -- так-же внятно отобразить сообщения. Неужели это не понятно?
0K>>Легче написать несколько строчек, чем читать все это и переливать из пустого в порожнее. Несколько строчек вашего кода могли бы убедить в вашей правоте лучше 100 тыс. слов. G>Я думаю тебя они бы ни в чем не убедили, потому что тебе надо не работающую программу получить, а что-то кому-то доказать. G>Приведи требования (полные и непротиворчеивые в таком масштабе это просто) и будет тебе программа.
Здравствуйте, GarryIV, Вы писали:
GIV>А если файл находится где то в сети надо выдать сообщение "Проверьте настроки сетевого подключения"?
Зачем? Нет, конечно. Попробуйте одной из MS-овских программ открыть файл из сети с выключенной сетью. Сообщение будет таким же, как если бы файл был на диске.
Здравствуйте, GarryIV, Вы писали:
0K>>Так и нужно писать: файл поврежден, желаете ли создать новый файл?
GIV>А если файл находится где то в сети надо выдать сообщение "Проверьте настроки сетевого подключения"?
Вот именно. Я даже больше скажу — часть исключений вообще нормально не обработать
Так, в данном примере — если кто-то посторонний открыл конфиг на редактирование в блокноте — мы получим при записи в него ошибку "файл занят иным процессом". При этом — это будет самое заурядное "IOException" ничем кроме .Message от прочих не отличающееся. То есть даже захочешь как-то специфически обработать — не выйдет.
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, gandjustas, Вы писали:
0K>>>Нет, по условию если файла нет -- нужно создать. G>>Если файла нет, то читающая программа в принципе не работает.
0K>Так вот чтобы пользователь не создавал его вручную -- позаботьтесь об этом и создайте файл.
0K>>>Опять-таки нет. Чужие ошибки не дают нам права делать плохо. G>>Не в этом вопрос, а в том что при неизвестной логике "другой программы" делать что-то бессмысленно.
0K>А зачем вам логика это другой программы? Ваша задача считать из файла число и записать его инкремент. Если файла не существует -- создать. Причем сделать максимально удобно для пользователя (если ошибся пользователь -- внятно пояснить и дать возможность повторить ввод). Если файл будет заблокирован/не доступен и пр -- так-же внятно отобразить сообщения. Неужели это не понятно?
0K>>>Легче написать несколько строчек, чем читать все это и переливать из пустого в порожнее. Несколько строчек вашего кода могли бы убедить в вашей правоте лучше 100 тыс. слов. G>>Я думаю тебя они бы ни в чем не убедили, потому что тебе надо не работающую программу получить, а что-то кому-то доказать. G>>Приведи требования (полные и непротиворчеивые в таком масштабе это просто) и будет тебе программа.
0K>Выше еще раз написал.
Ну хватит уже жопой играть.
Давай сначала
Программа должна быть не демо, а реальной. Т.е. считайте что вы пишите реальный проект, которым будут пользоваться тысячи людей.
Есть какая-то "другая программа", про которую известно что она читает число из файла (как минимум по известному пути), при чтении "другая программа" блокирует файл (видимо писал её какой-то мудак).
Нужна утилита, которая будет увеличивать число в этом файле на единицу. Пользователем будет видимо IT специалист, который немного понимает в компьютерах и причинах сбоя, а также умеет гуглить. Программа будет запускаться в одном экземпляре в одно время.
Идеальный вариант здесь — тн Unix way, консольная программа, которая не требует участия пользователя при запуске, в случае успеха она просто прекращает работу, в случае неудачи выводит информацию об ошибке и завершается с errorlevel отличным от нуля.
Так как заданный файл создается один раз, то проще попросить админа создать его при установке программы, также позаботиться о необходимых правах.
код:
class Program
{
static void Main()
{
var filename = ConfigurationManager.AppSettings["FileName"];
var count = int.Parse(File.ReadAllText(filename)) + 1;
File.WriteAllText(filename, count.ToString());
}
}
Собственно все. Инфу об ошибке и errorlevel отличный от 0 обеспечит встроенный механизм unhandled exception.
Здравствуйте, 0K, Вы писали:
N_P>>Просто если к старту юзерской программы к ней должен быть нормальный, существующий и корректно разбирающийся конфиг — не дело юзера разбираться почему даже "file not found", не говоря уж о "ошибка при чтении XML, строка 5, позиция 4" потому как ЛЮБОЙ факт ошибки чтения конфига в такой ситуации свидетельствует о создании нештатной ситуации и неизвестно — чего там еще не так.
0K>Так и нужно писать: файл поврежден, желаете ли создать новый файл?
А права у него на это есть — создавать файлы ? А иной информации в этих файлах не содержится ? Это навскидку — что у нас с таким подходом вылезет. А еще сеть может отвалиться на минутку и надо-бы попробовать раз 5 повторить чтение с задержкой.
Здравствуйте, Muxa, Вы писали:
M>поменьше слов, побольше кода. (c)
У меня вопросы по этому участку кода
try {
using (StreamReader reader = _fileInfo.OpenText()) {
if (!int.TryParse(reader.ReadToEnd(), out _value))
_messanger.WriteLine("Файл '{0}' поврежден. Значение счетчика сброшено.", _fileInfo.FullName);
reader.Close();
}
return;
}
catch (FileNotFoundException) {
_messanger.WriteLine("Файл '{0}' не найден и будет создан.", _fileInfo.FullName);
}
1. почему вы обещаете (через messenger) вещи, которые не делаете? Мне лень тестить, но по-моему вы не сбрасываете счётчик (остаётся предыдущее значение, нет?) да и файл вы создаёте в другом методе и по другому признаку. Т.е. например если моя реализация messenger'а выдаст здесь
_messanger.WriteLine("Файл '{0}' поврежден. Значение счетчика сброшено.", _fileInfo.FullName);
FileNotFoundException, то вы пообещаете создать файл, а на самом деле:
if (!_fileInfo.Exists)
using (FileStream stream = _fileInfo.Create()) stream.Close()
обманываете и не пытаетесь его создать. Моя реализация messenger'а может ведь писать в файл внутри WriteLine.
2. вы реально замарачиваетесь в своих проектах и инжектите такой messenger в каждый свой класс? или просто для примера сделали?
N>1. почему вы обещаете (через messenger) вещи, которые не делаете? Мне лень тестить, но по-моему вы не сбрасываете счётчик (остаётся предыдущее значение, нет?)
на счет сброса — дельное замечание. не проследил.
N>да и файл вы создаёте в другом методе и по другому признаку. Т.е. например если моя реализация messenger'а выдаст здесь
N> _messanger.WriteLine("Файл '{0}' поврежден. Значение счетчика сброшено.", _fileInfo.FullName);
N>
N>FileNotFoundException, то вы пообещаете создать файл, а на самом деле: N>
N> if (!_fileInfo.Exists)
N> using (FileStream stream = _fileInfo.Create()) stream.Close()
N>
N>обманываете и не пытаетесь его создать. Моя реализация messenger'а может ведь писать в файл внутри WriteLine.
тогда FileNotFoundException должен быть обработан внутри мессенжера и обернут в MessangerException, ящитаю. не?
(по крайней мере я бы так реализовал этого файлового мессенжера)
а тот FileNotFoundException вылетевший из reader = _fileInfo.OpenText(), просто проглатывается и об отсутствии файл сообщает пользователю, если пользователь попытается записать состояние счетчика, вот тут файл и будет создан. (а то вдруг пользователь передумает).
N>2. вы реально замарачиваетесь в своих проектах и инжектите такой messenger в каждый свой класс? или просто для примера сделали?
нет, конечно.
просто ТС, хотел абстрагированности от консольного применения и портабельности на WinForms и пр.
я предоставил ему необходимые интерфейсы.
G>class Program
G>{
G> static void Main()
G> {
G> var filename = ConfigurationManager.AppSettings["FileName"];
G> var count = int.Parse(File.ReadAllText(filename)) + 1;
G> File.WriteAllText(filename, count.ToString());
G> }
G>}
G>
G>Собственно все. Инфу об ошибке и errorlevel отличный от 0 обеспечит встроенный механизм unhandled exception.
Вы приходите на форум потоллить? Или как это понимать? Вы же больше всех выступали, что инфа в полях Exception не предназначена для пользователя (прямого вывода). А теперь сами вывели пользователю только системную информацию.
Задание вы не выпонили: требовалось запрашивать у пользователя имя счетчика (создавать если его нет) и требовалась возможность задейстовавать код в Win-приложении.
Ваш код не переносим, не юзабелен, вместо ошибок выводит непонятную простому пользователю абракадабру.
Ну и самое главное -- вы сами себе противоречите (на счет информации, выводимой пользователю).
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, gandjustas, Вы писали:
G>>
G>>class Program
G>>{
G>> static void Main()
G>> {
G>> var filename = ConfigurationManager.AppSettings["FileName"];
G>> var count = int.Parse(File.ReadAllText(filename)) + 1;
G>> File.WriteAllText(filename, count.ToString());
G>> }
G>>}
G>>
G>>Собственно все. Инфу об ошибке и errorlevel отличный от 0 обеспечит встроенный механизм unhandled exception.
0K>Вы приходите на форум потоллить? Или как это понимать? Вы же больше всех выступали, что инфа в полях Exception не предназначена для пользователя (прямого вывода). А теперь сами вывели пользователю только системную информацию.
Я вроде подробно описал юзкейсы. То что выводится — информация не для пользователя, а для админа. Для пользователя выводится ровно ничего.
0K>Задание вы не выпонили: требовалось запрашивать у пользователя имя счетчика (создавать если его нет) и требовалась возможность задейстовавать код в Win-приложении.
Нивопрос, давай свои юзкейсы, только чтобы они были риальными, мы же риальное приложение пишем
0K>Ваш код не переносим, не юзабелен, вместо ошибок выводит непонятную простому пользователю абракадабру.
Непереносим куда? Неюзабелен почему?
Ты прочитал описание? Для unix-way он более чем юзабелен и вроде как под моно должен отрабатывать.
0K>Ну и самое главное -- вы сами себе противоречите (на счет информации, выводимой пользователю).
Еще раз, в этом варианте вообще нет взаимодействия с пользователем. Так работают чет более чем все консольные утилиты в *nix.
Здравствуйте, Neco, Вы писали:
N>не согласен. я не отменяю исключения (не гашу их) — просто дополняю информацией. N>
N># It circumvents higher-level exception handling.
N>это с одной стороны звучит убедительно. но с другой стороны — на фига вышестоящему классу заботиться о моих ошибках? сейчас я работаю с файловой системой, а завтра изменю реализацию и буду работать с сокетами (т.е. опять же с инкрменентной целью, но уже по сети). а послезавтра меня осенит, что инкрементность можно возложить на оракл и просто брать оттуда значение следующего сиквенса
Не совсем так. Данная рекомендация говорит, что ты обрабатываешь все ошибки, которые могут возникнуть в твоем коде при нормальной работе и неизменными пробрасываешь дальше те ошибки, которые не относятся к собственно решаемой задаче.
То есть, у тебя явно тут вырисовываются исключения
1. формат числа (включая переполнение)
2. отсутствие файла
3. отсутсвие прав на изменение файла
4. отсутствие прав на добавление файлов в папку с файлами
5. Переполнение 32-битного числа счетчика при инкрементации.
Вот их надо отловить и завернуть в твой IncrementorException. В идеале с уточняющими полями, что именно было не так и с тем чтобы дальше иметь возможность показать пользователю сообщение различного вида в зависимости от проблемы.
А при переходе на сокеты или оракл — надо соответственно обдумать какие теперь могут возникать ошибки и отлавливать теперь уже их.
А например ThreadAbortException ловить и заворачивать в твое IncrementorException не стоит. А ведь вполне возможно, что этот инкрементатор будут запускать в отдельном потоке.
Так же, если ты действительно перейдешь на работу с Ораклом, причем на удаленном сервере — то получив сверху открытый коннекшен ты можешь предполагать, что он в процессе работы инкрементора он все время будет валидным, поскольку управление коннекшеном (если его передали сверху) не лежит в области ответственности инкрементора. А значит, не надо и пытаться обрабатывать ситуации с оторванным в процесе работы сетевым шнурком — эта обработка находится на уровень выше.
А вот исключение, что твой инкрементальный сиквенс отсутсвтует или уже переполнился — тебе надо отловить и обработать.
Здравствуйте, fmiracle, Вы писали:
F>Вот их надо отловить и завернуть в твой IncrementorException. В идеале с уточняющими полями, что именно было не так и с тем чтобы дальше иметь возможность показать пользователю сообщение различного вида в зависимости от проблемы.
Ага. Что-то начинаю понимать, но для полной ясности мне надо понять, что должен делать с этим вышестоящий класс. Не мог бы ты описать или пример привести?
N_P>>А еще сеть может отвалиться на минутку и надо-бы попробовать раз 5 повторить чтение с задержкой.
0K>Если пользователь дал добро создать файл -- значит можно затирать.
Да ни в коем случае! Он может быть просто некомпетентен в этом! Может там строка соединения к БД регистрации, которую админ перед началом работы комплекса настраивает спец-утилитой ? Юзер про то и знать-то не знает — скрыто это от него.
не знаю был ли тут этот вопрос, но
что если после чтения счетчик был изменен кем-то из вне? (напр. другим экземпляром программы)
надо ли при записи сравнивать значение счетчика в файле с прочитанным?
Здравствуйте, Muxa, Вы писали:
M>поменьше слов, побольше кода. (c)
Спасибо.
Теперь критика:
По существу:
1. Если файл при чтении занят другим процессом, возникает непредвиденная вашей программой ошибка:
Произошла непредвиденная ошибка. Обратитесь к разработчику.
System.IO.IOException: The process cannot access the file 'C:\Users\User\AppDat
a\Local\Temporary Projects\ConsoleApplication1\bin\Debug\f' because it is being
used by another process.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, I
nt32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions o
ptions, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolea
n useLongPath)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access,
FileShare share, Int32 bufferSize, FileOptions options)
at System.IO.StreamReader..ctor(String path, Encoding encoding, Boolean detec
tEncodingFromByteOrderMarks, Int32 bufferSize)
at System.IO.FileInfo.OpenText()
at ConsoleApplication1.FileIncrementor.Read() in C:\Users\User\AppData\Local
\Temporary Projects\ConsoleApplication1\Program.cs:line 76
at ConsoleApplication1.Program.Main() in C:\Users\User\AppData\Local\Tempora
ry Projects\ConsoleApplication1\Program.cs:line 18
Еще раз? (y/Y/д/Д/Ввод = ДА, остальное = НЕТ)
Не думаю, что из-за подобной чепухи нужно беспокоить разработчика. Программы должны работать а рарзработчик отдыхать
Так что, как видите, стратегия Neco перехватить базовый Exception на практике оказалась намного прочнее. Вы все-таки забыли обработать IOException (И не удивительно, т.к. посчитайте сколько их там этих исключений и нет ни единой возможности обработать все пачкой).
Дополнительно:
1. Неправильно наследуете Exception. Нужно имплеменитровать все конструкторы + пометить класс Serializable. Если добавляете свои свойства -- они должны быть сериализуемыми (у вас не нужно). Класс исключения должен быть public.
2. При string.Format вам нужно указать CultureInfo.InvariantCulture, иначе все преобразуется с поправкой на текущую культуру. Это обязательно писать.
Здравствуйте, Nikolay_P_I, Вы писали:
N_P>Да ни в коем случае! Он может быть просто некомпетентен в этом! Может там строка соединения к БД регистрации, которую админ перед началом работы комплекса настраивает спец-утилитой ? Юзер про то и знать-то не знает — скрыто это от него.
А вообще при недоступности сети будет не FileNotFoundException... Так что проблема надумана.
0K>1. Неправильно наследуете Exception. Нужно имплеменитровать все конструкторы + пометить класс Serializable. Если добавляете свои свойства -- они должны быть сериализуемыми (у вас не нужно). Класс исключения должен быть public.
стыдно спросить, но зачем? (все конструкторы, сериализуемость, и паблик (это, кстати, решарпер изменил на интёрнал))
Здравствуйте, gandjustas, Вы писали:
G>Нивопрос, давай свои юзкейсы, только чтобы они были риальными, мы же риальное приложение пишем
Все что нужно было сделать -- описано в первом и втором сообщении. Как минимум 4 человека прекрасно меня поняли и даже реализовали требования в виде кода. Вы же только всех критикуете.
Специально для таких как вы, напишу по пунктам. Надеюсь диаграммы не заставите рисовать
Итак. Нужна библиотека, которую можно задействовать в Windows или консольном приложении. Желательно без особых заморочек (как подписывание на события). Кроме того, нужно продемонстровать использование этой библиотеки с консольным приложением (нужен код консольного приложения и библиотеки). Это требование было во втором сообщении.
Как все должно работать:
1. Вводим название счетчика. Название счетчика должно соответствовать файлу в любой из папок (можно использовать текущую папку проекта -- не важно). Нельзя допускать, чтобы 2 счетчика с разным названием имели один и тот же файл.
2. Если название счетчика некорректно, выдаем сообщение об ошибке (можно любое не тратьте время на обдумывание текста, его заменят другие) и предоставляем возможность ввести другое название.
3. Если файл со счетчиком не существует -- спрашиваем создать ли его. Разрешение получено -- создаем, иначе -- датем возможность ввести другое имя счетчика (без выхода из программы).
4. Пытаемся прочесть файл. Если прочесть не удалось -- вывести 2 сообщения: для обычных пользователей(ака проблема с чтением файла) и детали для админа (почему не удалось прочесть). Предоставить возможность повторить повторить попытку чтения или выйти из программы/начать все заново.
5. Пытаемся преобразовать текст из файла к целому положительному числу. Если преобразовать не удалось или если файл пустой (новый) -- даем возможность установить значение счетчика (для упрощения можно автоматически устанавливать 0).
6. Увеличиваем число, прочтенное из файла, на 1.
7. Сохраняем новое значение числа в тот же файл, из которого прочли. Если сохранить не удалось -- вывести 2 сообщения: для обычных пользователей(ака проблема с чтением файла) и детали для админа (почему не удалось прочесть).
8. Дать возможность повторить процесс.
Все это умные люди поняли из первого сообщения и даже привели решения.
Здравствуйте, Muxa, Вы писали:
M>стыдно спросить, но зачем? (все конструкторы, сериализуемость, и паблик (это, кстати, решарпер изменил на интёрнал))
Public, т.к. исключение может "выйти" за пределы вашей библиотеки. Просто забудете его перехватить -- и все. А как его ловить выше? По базовому Exception?
Сериализация -- нужна, т.к. исключения иногда "выбрасывают" удаленно (в другом домене или вообще на другой машине в другой части света ). Это просто рекомендация MS, в нашей программе это смысла не имеет.
Все конструкторы -- не знаю сам -- это рекомендация MS. Разве что protected-конструктор, который используется в сериализации закрывать не стоит. Да и то, если ваш Exception sealed и вы явно не добавляете новых сериализуемых свойств -- смысла нет.
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, Muxa, Вы писали:
M>>стыдно спросить, но зачем? (все конструкторы, сериализуемость, и паблик (это, кстати, решарпер изменил на интёрнал))
0K>Public, т.к. исключение может "выйти" за пределы вашей библиотеки. Просто забудете его перехватить -- и все. А как его ловить выше? По базовому Exception?
Нет требований к видимости исключений. Это самодеятельность. Может "выйти" а может и не "выйти".
0K>Сериализация -- нужна, т.к. исключения иногда "выбрасывают" удаленно.
Иногда код вызывают удаленно.
0K> Это просто рекомендация MS, в нашей программе это смысла не имеет.
Это не рекомендация, это требование. Смысл имеет, т.к. любой код может быть вызван удаленно.
0K>Все конструкторы -- не знаю сам -- это рекомендация MS.
Это не рекомендация, это посильнее.
0K>Разве что protected-конструктор, который используется в сериализации закрывать не стоит. Да и то, если ваш Exception sealed и вы явно не добавляете новых сериализуемых свойств -- смысла нет.
Без спецконструктора не будет сериалиации, в том числе у наследников. Спецконструктор — требование.
Здравствуйте, 0K, Вы писали:
0K>Как все должно работать:
Наконец, удалось вымучить описание задачи. И двух дней не прошло.
Можно сравнить с первым сообщением, и увидеть, как сильно они отличаются.
И как повлияли предложенные решения и дискуссия на формулировки в описании.
0K>Все это умные люди поняли из первого сообщения и даже привели решения.
Unhandled Exception: Обнаружена попытка надавить на эмоции. Игнорируется.
M>>стыдно спросить, но зачем? (все конструкторы, сериализуемость, и паблик (это, кстати, решарпер изменил на интёрнал))
0K>Public, т.к. исключение может "выйти" за пределы вашей библиотеки. Просто забудете его перехватить -- и все. А как его ловить выше? По базовому Exception?
ааа, это если проект оформлять в виде exe+dll тогда конечно (просто только сейчас нашел комментарий что должна быть отдельная сборка работы со счетчиками). я же отлавливаю свои исключения в вызывающем коде, если б солюшен был бы в виде программа+сборка, то и исключение было бы публичное, иначе синтакс еррор.
если бы я был автором только сборки, когда необходимо предусмотреть, что автор вызывающего кода захочет перехватывать мои исключения, и сделать их заранее публичными.
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, gandjustas, Вы писали:
G>>Нивопрос, давай свои юзкейсы, только чтобы они были риальными, мы же риальное приложение пишем
0K>Все что нужно было сделать -- описано в первом и втором сообщении. Как минимум 4 человека прекрасно меня поняли и даже реализовали требования в виде кода. Вы же только всех критикуете.
0K>Специально для таких как вы, напишу по пунктам. Надеюсь диаграммы не заставите рисовать
0K>Итак. Нужна библиотека, которую можно задействовать в Windows или консольном приложении. Желательно без особых заморочек (как подписывание на события). Кроме того, нужно продемонстровать использование этой библиотеки с консольным приложением (нужен код консольного приложения и библиотеки). Это требование было во втором сообщении.
0K>Как все должно работать:
0K>1. Вводим название счетчика. Название счетчика должно соответствовать файлу в любой из папок (можно использовать текущую папку проекта -- не важно). Нельзя допускать, чтобы 2 счетчика с разным названием имели один и тот же файл. 0K>2. Если название счетчика некорректно, выдаем сообщение об ошибке (можно любое не тратьте время на обдумывание текста, его заменят другие) и предоставляем возможность ввести другое название. 0K>3. Если файл со счетчиком не существует -- спрашиваем создать ли его. Разрешение получено -- создаем, иначе -- датем возможность ввести другое имя счетчика (без выхода из программы). 0K>4. Пытаемся прочесть файл. Если прочесть не удалось -- вывести 2 сообщения: для обычных пользователей(ака проблема с чтением файла) и детали для админа (почему не удалось прочесть). Предоставить возможность повторить повторить попытку чтения или выйти из программы/начать все заново. 0K>5. Пытаемся преобразовать текст из файла к целому положительному числу. Если преобразовать не удалось или если файл пустой (новый) -- даем возможность установить значение счетчика (для упрощения можно автоматически устанавливать 0). 0K>6. Увеличиваем число, прочтенное из файла, на 1. 0K>7. Сохраняем новое значение числа в тот же файл, из которого прочли. Если сохранить не удалось -- вывести 2 сообщения: для обычных пользователей(ака проблема с чтением файла) и детали для админа (почему не удалось прочесть). 0K>8. Дать возможность повторить процесс.
0K>Все это умные люди поняли из первого сообщения и даже привели решения.
Во-первых ты снова описываешь что должна делать программа, а я спрашиваю зачем. Ты думаешь в в риальном случае будет приходить заказчик и рассказывать о каких-то там файлах в папках?
Во-вторых если нужна библиотека, как ты сам написал, то никакого user interaction там не будет в принципе, поэтому "как все должно работать" идет лесом.
Опиши цели (для пользователя) и юзкейсы, тогда продолжим.
Здравствуйте, Neco, Вы писали:
N>Здравствуйте, fmiracle, Вы писали:
F>>Вот их надо отловить и завернуть в твой IncrementorException. В идеале с уточняющими полями, что именно было не так и с тем чтобы дальше иметь возможность показать пользователю сообщение различного вида в зависимости от проблемы. N>Ага. Что-то начинаю понимать, но для полной ясности мне надо понять, что должен делать с этим вышестоящий класс. Не мог бы ты описать или пример привести?
Класс будет делать то, что ему надо и что диктуется общей логикой программы. На примере рассматривамой программки это неясно, но должно быть яснее, если этот инкрементатор — это только часть более общей системы. И тогда удобно, что исключения из различных источников не перекрывают друг друга.
Ну вот рассмотрим как раз пример, что инкрементор стал реализован через Оракл (или даже так же реализован через файлы, с опциональной настройкой где-то в конфигурации какое решение использовать). При этом работа с доступом управляется извне класса (это нормально — например, для проведения изменений, созданных классом в общей транзакции)
код не реальный, а только ключевые моменты выделены с заметными упрощениями
class OracleIncrementor : IIncrementor
{
public Increment()
{
var dbManager = Ioc.Resolve<IDbManager>();
//провести инкрементацию чего надо через dbManager.
}
}
class Main
{
public User LoginUser( string login, string pass )
{
try
{
var dbManager = Ioc.Resolve<IDbManager>();
var user = dbManager.SearchUserByLoginAndPass( login, pass );
if( user == null )
{
throw new NoAccessException("Пользователь с таким логином или паролем не зарегистрирован в системе");
}
using( var tran = dbManager.BeginTransaction() )
{
//у пользователя обновляем дату последного захода в систему
user.LastLoginTime = DateTime.Now;
dbManager.Save(user);
//при логине мы так же обновляем специальный сторонний счетчик заходовtry
{
var incrementor = Ioc.Resolve<IIncrementor>( "userLogons" );
incrementor.Increment();
}
catch( IncrementorException iex )
{
//тут решаем что делать с ошибкой инкрементации.
//считается, что сюда попадают только те ошибки, которые не смог решить сам инкрементор.
//Если нам эта инкрементация не очень-то и важна, то просто пишем в лог и подавляем ошибку.
//а если важна (может быть для какой-то интеграции) и работа без нее продолжена быть не может
//то этого блока try/catch тут просто не будет и ошибка сама собой уйдет выше.
}
tran.CommitTransaction();
return user;
}
}
catch( DbException dex )
{
//а вот это уже для нас серьезно - у нас сбой базы данных. То ли не можем приконнектиться
//то ли структура базы не соответствует ожидаемой
//в общем - надо думать
//как минимум можно переложить идею подумать пользователю, вывалив ему эту ошибку наверх
//а можно попробовать покопаться в кишках DbException и понять
//что именно у нас произошло, и можно ли сделать что-то для исправления
//(например, у нас может быть требование к программе, что при сбое соединения, надо подождать,
//и еще два раза попробовать соединиться, и только потом уже отключаться).
//или можно запустить обновлятор базы и если он успешно что-то обновит - то попробовать провести повторный логин.
//и совсем даже не факт, что это должно обрабатываться в данном методе а не где-то выше.
}
}
}
В твоем варианте последняя DbException, если возникала в случае работы с Инкрементором (например, при выдергивании шнурка из сервера во время обработки) пришла бы наверх в виде IncrementoException и попала бы в ветку обработки, в которой не предполагалось решать какие-либо проблемы с базой.
При этом, например, ошибка, что сиквенс, который мы используем для ведения инкрементации есть, но его значение уже равно максимальному — это IncrementorException, и описываеся оно как "Достигнуто максимальное значение, дальнейшее увеличение невозможно".
А вот ошибка, что последовательность вообще отсутствует — это скорее всего не IncrementorException, потому что с большой вероятностью — это общая проблема с базой. Очень вероятно, что просто подключились не к той базе. Или структура база не была обновлена до нужной версии приложения. И обрабатывать эту проблему надо наравне с остальными проблемами несоответствия структуры базы ожидаемой.
Здравствуйте, 0K, Вы писали:
0K>1. Если файл при чтении занят другим процессом, возникает непредвиденная вашей программой ошибка:
0K>
0K>Произошла непредвиденная ошибка. Обратитесь к разработчику.
0K>System.IO.IOException: The process cannot access the file 'C:\Users\User\AppDat
0K>a\Local\Temporary Projects\ConsoleApplication1\bin\Debug\f' because it is being
0K>used by another process.
....
0K>Еще раз? (y/Y/д/Д/Ввод = ДА, остальное = НЕТ)
0K>Не думаю, что из-за подобной чепухи нужно беспокоить разработчика. Программы должны работать а рарзработчик отдыхать
0K>Так что, как видите, стратегия Neco перехватить базовый Exception на практике оказалась намного прочнее. Вы все-таки забыли обработать IOException (И не удивительно, т.к. посчитайте сколько их там этих исключений и нет ни единой возможности обработать все пачкой).
Вообще-то, принципиальной разницы нету, поскольку тут тоже стоит обработка базового Exception. Программа не упала, ничего не испортила, просто посоветовала обратиться к разработчику и написала подробную информацию об ошибке. Вариант Neco в этом случае написал бы ровно то же самое, но другими словами, что не мудрено, поскольку оперирует тем же IOException.Message и выводит IOException в качестве деталей.
Серьезная разница только в том, что Neco не рекомендует обращаться к разработчику и не называет ошибку, которую он не продумывал, "непредвиденной". Да, это очень, очень серьезная разница. Сон разработчика — дело святое.
Здравствуйте, akasoft, Вы писали:
A>Читаем дальше. В строке 04 видим разбор полученного на предыдущем шаге значения. Тут мы можем словить FormatException, если окажется, что то, что мы проситали — не число. Реагируем на из предположения (тут включилась Боевая Телепатия, в коде про это ничего нет!), что если там чёртичто, то должен быть сбоку бантик, т.е. "-1":
Еще может быть OverflowException, если там число, но очень, очень большое.
Но что с ним делать автор все равно не рассказал... Я лично и этот случай (как и некорректный формат) понял так, что ничего писать не надо. А ты понял прямо наоборот.
Видимо это все потому (как утверждает автор в первых постах темы), что мы с тобой тупые кодеры, за которых надо думать
Здравствуйте, Muxa, Вы писали:
M>не знаю был ли тут этот вопрос, но M>что если после чтения счетчик был изменен кем-то из вне? (напр. другим экземпляром программы) M>надо ли при записи сравнивать значение счетчика в файле с прочитанным?
И что делать, если между сравнением счетчика в файле при записи и собственно записью, произошло опять же стороннее изменение? Может надо еще раз проверить соответствие?
F>И что делать, если между сравнением счетчика в файле при записи и собственно записью, произошло опять же стороннее изменение? Может надо еще раз проверить соответствие?
алгоритм записи:
открыть файл на чтение-запись, сравнить, если все ок, записать, иначе "хз".
так никто не вклинится.
осталось понять чему равно "хз".
Здравствуйте, Muxa, Вы писали:
F>>И что делать, если между сравнением счетчика в файле при записи и собственно записью, произошло опять же стороннее изменение? Может надо еще раз проверить соответствие? M>алгоритм записи: M>открыть файл на чтение-запись, сравнить, если все ок, записать, иначе "хз". M>так никто не вклинится. M>осталось понять чему равно "хз".
Ну тогда надо открыть на чтение/запись сразу же еще при первом обращении за значением счетчика (операция-то над данными простая и недлительная — распарсить и инкрементировать). А "хз" будет у той абстрактной программы, которая с этим файлом работает и попробует в этот момент обратиться к файлу
Но это всегда может быть — все равно какое-то время в файл пишем.
Здравствуйте, fmiracle, Вы писали:
F>И что делать, если между сравнением счетчика в файле при записи и собственно записью, произошло опять же стороннее изменение? Может надо еще раз проверить соответствие?
Лучше всего запретить другим процессам открывать файл (кстати, MS Office так и делает).
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, samius, Вы писали:
S>>Нет требований к видимости исключений. Это самодеятельность. Может "выйти" а может и не "выйти".
0K>Ошибаетесь. FxCop исправляет эту ошибку и дает ссылку на священное писание (MSDN).
И в отличие от "CA2229: Implement serialization constructors", которое ты назвал рекомендацией
Do not suppress a violation of the rule.
, по поводу "CA1064: Exceptions should be public" написано:
Suppress a message from this rule if you are sure in all cases that the private exception will be caught within its own internal scope
Здравствуйте, gandjustas, Вы писали:
G>Во-первых ты снова описываешь что должна делать программа, а я спрашиваю зачем.
Когда вы в магазин приходите, продавец вас тоже спрашивает "зачем"? Какая исполнителю разница "зачем"? Ему сказали -- сделал. А зачем -- это уже заказчику нужно.
G>Ты думаешь в в риальном случае будет приходить заказчик и рассказывать о каких-то там файлах в папках?
Да, есть такие заказчики. Продвинутые. Причем спорить с ними бесполезно.
G>Опиши цели (для пользователя) и юзкейсы, тогда продолжим.
А цели я вам называл. Есть сторонняя программа, которая сканирует с определенным интервалом папку в поисках файлов счетчиков. Если файлы есть -- читает из них значение и исполняет. Если значение изменилось на 1 -- опять исполняет. И иногда может подпортить файл. Причем таких счетчиков нужно много -- вручную лучше не создавать.
А что это за вторая программа -- большая тайна.
Думаю достаточно информации, чтобы написать. Уже по пунктам расписал. Даже примеры есть 4 штуки и вы же сами на ошибки указывали. Так возьмите и напишите как правильно
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, gandjustas, Вы писали:
G>>Во-первых ты снова описываешь что должна делать программа, а я спрашиваю зачем.
0K>Когда вы в магазин приходите, продавец вас тоже спрашивает "зачем"? Какая исполнителю разница "зачем"? Ему сказали -- сделал. А зачем -- это уже заказчику нужно.
Аналогии идут лесом.
G>>Ты думаешь в в риальном случае будет приходить заказчик и рассказывать о каких-то там файлах в папках?
0K>Да, есть такие заказчики. Продвинутые. Причем спорить с ними бесполезно.
G>>Опиши цели (для пользователя) и юзкейсы, тогда продолжим.
0K>А цели я вам называл. Есть сторонняя программа, которая сканирует с определенным интервалом папку в поисках файлов счетчиков. Если файлы есть -- читает из них значение и исполняет. Если значение изменилось на 1 -- опять исполняет. И иногда может подпортить файл.
И что потом делает с подпорченным файлом? Просто кладет йух? Как определяет что изменилось ровно на 1?
Это очень важные сведения, не отмахивайся от них.
0K>Причем таких счетчиков нужно много -- вручную лучше не создавать.
Все равно ограниченное количество и конкретные файлы, а не произвольные, вводимые пользователем.
0K>Думаю достаточно информации, чтобы написать. Уже по пунктам расписал. Даже примеры есть 4 штуки и вы же сами на ошибки указывали. Так возьмите и напишите как правильно
Нет, ты еще не описал юзкейсы для того что надо написать.
И снова пропустил главное, зачем оно нужно пользователю.
Здравствуйте, gandjustas, Вы писали:
G>И что потом делает с подпорченным файлом? Просто кладет йух? Как определяет что изменилось ровно на 1? G>Это очень важные сведения, не отмахивайся от них.
Если он их портит -- то 99.999999% там записан набор байт, не являющийся текстовым представлением числа. Если файл испорчен оповестить пользователя и дать возможность ввести новое значение (для упрощения можно автоматически вписать нуль).
0K>>Причем таких счетчиков нужно много -- вручную лучше не создавать. G>Все равно ограниченное количество и конкретные файлы, а не произвольные, вводимые пользователем.
Ну и что. Все равно их удобнее создавать через программу. И программа должна уметь это делать.
0K>>Думаю достаточно информации, чтобы написать. Уже по пунктам расписал. Даже примеры есть 4 штуки и вы же сами на ошибки указывали. Так возьмите и напишите как правильно G>Нет, ты еще не описал юзкейсы для того что надо написать.
G>И снова пропустил главное, зачем оно нужно пользователю.
Для удобства управления той второй секретной программой.
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, gandjustas, Вы писали:
G>>И что потом делает с подпорченным файлом? Просто кладет йух? Как определяет что изменилось ровно на 1? G>>Это очень важные сведения, не отмахивайся от них.
0K>Если он их портит -- то 99.999999% там записан набор байт, не являющийся текстовым представлением числа. Если файл испорчен оповестить пользователя и дать возможность ввести новое значение (для упрощения можно автоматически вписать нуль).
)Ты сказал что программа отслеживает увеличение на 1, но при этом может испортить файл. Как это совмещается? Программа испортила, потом я туда записал 0, сработает?
Может эмулятор напишешь?
0K>>>Причем таких счетчиков нужно много -- вручную лучше не создавать. G>>Все равно ограниченное количество и конкретные файлы, а не произвольные, вводимые пользователем.
0K>Ну и что. Все равно их удобнее создавать через программу. И программа должна уметь это делать.
Ну это уже я буду решать что она должна делать, твое дело предоставить полные и непротиворечивые юзкейсы.
G>>И снова пропустил главное, зачем оно нужно пользователю. 0K>Для удобства управления той второй секретной программой.
Что значит "удобство"?
Здравствуйте, gandjustas, Вы писали:
0K>>Ну и что. Все равно их удобнее создавать через программу. И программа должна уметь это делать. G>Ну это уже я буду решать что она должна делать, твое дело предоставить полные и непротиворечивые юзкейсы.
Нет уж. Вам было сказано что нужно сделать -- это мне решать. Выполните требования в точности -- и получите награду. Если требования не полны -- это моя проблема.
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, gandjustas, Вы писали:
0K>>>Ну и что. Все равно их удобнее создавать через программу. И программа должна уметь это делать. G>>Ну это уже я буду решать что она должна делать, твое дело предоставить полные и непротиворечивые юзкейсы.
0K>Нет уж. Вам было сказано что нужно сделать -- это мне решать. Выполните требования в точности -- и получите награду. Если требования не полны -- это моя проблема.
Ты же сам хотел по-взрослому, а теперь въезжаешь. Видимо программа не нужна, раз ты не можешь SRS предоставить, даже с моей помощью. Значит код такой:
//пусто
Повторяй до просветления: Качество кода зависит от решаемых им задач. Не существует "правильной обработки исключений в вакууме", про которую ты тут всем пытаешься мозг сделать.
Здравствуйте, gandjustas, Вы писали:
G> G>Ты же сам хотел по-взрослому, а теперь въезжаешь. Видимо программа не нужна, раз ты не можешь SRS предоставить, даже с моей помощью. Значит код такой:
Вы просто боитесь опозориться. Это стало понятно после нескольких постов. Если бы идея казалась вам глупой -- вы бы не критиковали тех, кто написал код. А раз указывали на их ошибки -- значит поняли что требуется.
G>Повторяй до просветления: Качество кода зависит от решаемых им задач. Не существует "правильной обработки исключений в вакууме", про которую ты тут всем пытаешься мозг сделать.
Задача расписана по пунктам, понятнее нельзя. Если есть конкретные вопросы -- задавайте, отвечу из принципа.
На врядли станете это делать, т.к. знаете что я опозорю ваш код. А то как обычно -- кто больше всех говорит -- хуже всех делает.
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, gandjustas, Вы писали:
G>> G>>Ты же сам хотел по-взрослому, а теперь въезжаешь. Видимо программа не нужна, раз ты не можешь SRS предоставить, даже с моей помощью. Значит код такой:
0K>Вы просто боитесь опозориться. Это стало понятно после нескольких постов.
Так ты еще и психолог?
0K>Если бы идея казалась вам глупой -- вы бы не критиковали тех, кто написал код. А раз указывали на их ошибки -- значит поняли что требуется.
Я указал возможные ошибки еще до того как написали первый вариант, но без понимания зачем код это все пустой разговор. Как уже говорил подобный кож можно усложнять до бесконечности не зная решаемых задач.
G>>Повторяй до просветления: Качество кода зависит от решаемых им задач. Не существует "правильной обработки исключений в вакууме", про которую ты тут всем пытаешься мозг сделать.
0K>Задача расписана по пунктам, понятнее нельзя. Если есть конкретные вопросы -- задавайте, отвечу из принципа.
Ты расписал решение, а не задачу. Задачу ты так ни разу и не написал.
0K>На врядли станете это делать, т.к. знаете что я опозорю ваш код.
Я уже написал код. http://rsdn.ru/forum/dotnet/3923729.aspx
Можешь прям щас начинать Я же специально написал все решения которые принял при разработке этого кода и юзкейсы.
Ты же начал выдумывать свою задачу, но почему-то не закончил... Выдумай свою задачу, тогда поговорим. Но надо чтобы она была риальной, как ты сам хочешь.
Здравствуйте, 0K, Вы писали:
0K>На врядли станете это делать, т.к. знаете что я опозорю ваш код. А то как обычно -- кто больше всех говорит -- хуже всех делает.
Самокритично
Здравствуйте, gandjustas, Вы писали:
0K>>Задача расписана по пунктам, понятнее нельзя. Если есть конкретные вопросы -- задавайте, отвечу из принципа. G>Ты расписал решение, а не задачу. Задачу ты так ни разу и не написал.
Вам как разработчику задачу знать не обязательно. Пишите код на основании тех требований, которые приведены.
А задача связана с работой второй программы. Но вам это не нужно.
0K>>На врядли станете это делать, т.к. знаете что я опозорю ваш код. G>Я уже написал код. http://rsdn.ru/forum/dotnet/3923729.aspx
G>Можешь прям щас начинать Я же специально написал все решения которые принял при разработке этого кода и юзкейсы.
Есть конкретные требования, которые вы не выполнили. Из чего я делаю вывод -- что вы просто приходите сюда пофлеймить. С такими людьми как вы работать -- равносильно плоному провалу. Делать ничгео не умеете, всех критикуете, времени на вас уходит уйма.
Вам задача ясна, что было видно из вашей критики других участников. А придираться и требовать схем, более четких и непротиворечивых объяснений -- это участь леньтяев и неудачников, которые реально ничего делать не умеют.
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, gandjustas, Вы писали:
0K>>>Задача расписана по пунктам, понятнее нельзя. Если есть конкретные вопросы -- задавайте, отвечу из принципа. G>>Ты расписал решение, а не задачу. Задачу ты так ни разу и не написал.
0K>Вам как разработчику задачу знать не обязательно. Пишите код на основании тех требований, которые приведены. 0K>А задача связана с работой второй программы. Но вам это не нужно.
Ты окончательно слился.
Прочитал все ветки полностью. Возможно ошибаюсь, но мне кажется автор просто хочет решить поставленную перед ним задачу с помощью участников форума. (ИМХО)
Касательно задачи, давайте попробуем написать требования к библиотеки, раз уж народ так просит и никак не может дождаться.
Пока обойдемся без пользовательского интерфейса. Какой он будет ведь разницы нет.
Описание задачи:
Библиотека получает на вход название счетчика. Ищет/создает нужный счетчик, инкрементирует счетчик на 1. В случае, если данные счетчика не корректны, то сбросить инкремент в 0.
Тип счетчика — Int32. При переполнении числа сбросить на 0.
На момент изменения записи данные должны блокироваться для других клиентов
Т.к. источник данных может быть разный (файловое хранилище, БД и т.д.), то для ошибочных ситуаций создать типы возможных исключений, которые будут подниматься на верхний уровень.
Поле Message исключения должно нести достаточную информацию для программистов, но не пугающую конечного пользователя (Если исключение не перехватят на верхнем уровне, то его возможно увидит пользователь). Вся дополнительная информация должна передаваться в InnerException.
Персонально для ОК: Типов возможных исключений бояться не надо. Их не будет сотни-мульён, как вы это представляете. К примеру, для работы с файлами их порядка 10. (MS описала 10 ситуаций — не развалилось. Ну значит и вы сможете)
Клиент консольное приложение — забить
Клиент win-приложение — забить
Клиент web-приложение — забить
Клиент другая библиотека — забить
Другими словами, нам похер, кто использует данную библиотеку.
Если требований не достаточно, то дописать/изменить.
Для OK: Предлагаю написать для общества ТЗ на основании ТТ.
Когда напишете ТЗ, тогда вам накатают код не задавая дополнительных вопросов.
Здравствуйте, mrjeka, Вы писали:
M>Т.к. источник данных может быть разный (файловое хранилище, БД и т.д.), то для ошибочных ситуаций создать типы возможных исключений, которые будут подниматься на верхний уровень.
При таких условиях написать единую библиотеку не выйдет никак, потому что для файлов это будет код, а для БД просто запрос.
Поэтому предлагаю таки ограничиться файлами.
Если писать библиотеку для повторного использования, то стоит написать так:
void Increment(string fileName)
{
using(var f = File.Open(fileName, FileMode.OpenOrCreate))
{
var reader = new StreamReader(f);
var content = reader.ReadToEnd();
var counter = 0;
if(int.TryParse(content, out counter))
{
counter = (counter == int.MaxValue) ? 0 : (counter+1)
}
f.Seek(0, SeekOrigin.Begin);
f.SetLength(0);
var writer = new StreamWriter(f);
writer.Write(counter);
}
}
Здравствуйте, gandjustas, Вы писали:
G>Здравствуйте, mrjeka, Вы писали:
M>>Т.к. источник данных может быть разный (файловое хранилище, БД и т.д.), то для ошибочных ситуаций создать типы возможных исключений, которые будут подниматься на верхний уровень.
G>При таких условиях написать единую библиотеку не выйдет никак, потому что для файлов это будет код, а для БД просто запрос. G>Поэтому предлагаю таки ограничиться файлами.
Позволю себе не согласиться. Например в asp.net введена модель провайдеров. Пашет замечательно. Данные хранятся в БД(MS SQL, Oracle), ActiveDirectory, да в прочем где угодно.
Здравствуйте, mrjeka, Вы писали:
M>Здравствуйте, gandjustas, Вы писали:
G>>Здравствуйте, mrjeka, Вы писали:
M>>>Т.к. источник данных может быть разный (файловое хранилище, БД и т.д.), то для ошибочных ситуаций создать типы возможных исключений, которые будут подниматься на верхний уровень.
G>>При таких условиях написать единую библиотеку не выйдет никак, потому что для файлов это будет код, а для БД просто запрос. G>>Поэтому предлагаю таки ограничиться файлами.
M>Позволю себе не согласиться. Например в asp.net введена модель провайдеров. Пашет замечательно. Данные хранятся в БД(MS SQL, Oracle), ActiveDirectory, да в прочем где угодно.
M>Вся работа ведется через базовый провайдер.
Я говорю о том что для такого простого действия нет смысла городить модель провайдеров, кроме того реализация интересует больше архитектуры в данном вопросе.
А сделать IЧтоТоТам и реализовать методы несложно.
Здравствуйте, gandjustas, Вы писали:
G>Здравствуйте, mrjeka, Вы писали:
M>>Здравствуйте, gandjustas, Вы писали:
G>>>Здравствуйте, mrjeka, Вы писали:
M>>>>Т.к. источник данных может быть разный (файловое хранилище, БД и т.д.), то для ошибочных ситуаций создать типы возможных исключений, которые будут подниматься на верхний уровень.
G>>>При таких условиях написать единую библиотеку не выйдет никак, потому что для файлов это будет код, а для БД просто запрос. G>>>Поэтому предлагаю таки ограничиться файлами.
M>>Позволю себе не согласиться. Например в asp.net введена модель провайдеров. Пашет замечательно. Данные хранятся в БД(MS SQL, Oracle), ActiveDirectory, да в прочем где угодно.
M>>Вся работа ведется через базовый провайдер.
G>Я говорю о том что для такого простого действия нет смысла городить модель провайдеров, кроме того реализация интересует больше архитектуры в данном вопросе.
Ну и самое главное забыл. Если интересует именно обработка исключительных ситуаций, то лучше не пытаться причесать все к одному виду, для БД и файлов они будут капитально разные, вплоть до разных UI.
Здравствуйте, gandjustas, Вы писали:
G>Ну и самое главное забыл. Если интересует именно обработка исключительных ситуаций, то лучше не пытаться причесать все к одному виду, для БД и файлов они будут капитально разные, вплоть до разных UI.
Я предлагаю сделать следующее по таким соображениям: В предложенном примере пользователя библиотеки я бы хотел ограничить умеренным числом возможных исключений (если библиотека может работать с разными источниками данных). Т.е. исключение несет в себе сообщение, в innerException подробности (фактически причина возникновения исключения). Надеюсь понятно объяснил.
Что до реализации, то если опереться на пример кода, приведенный автором, то обернуть всё тело метода майн в try/(и все возможные catch и делов).
Как говорится какой привет, такой ответ.
Поэтому я автору предлагаю написать нормальную формулировку задачи, т.к. у многих тут куча вопросов вроде как где и почему.
детализировать задачи.
Работа с исключениями в UI
Работа с исключениями в DLL
ну подумать, может еще какие либо ситуации придумает.
Вот например в вашем ответе есть недочет. Я согласен с тем, что надо поднимать исключения наверх. Но в методе существуют еще и свои условия, по которым выполнение операции невозможно. Если это метод библиотеки, то в этой ситуации надо поднимать (кастомное) исключение, которого нет в вашем примере. Если это метод UI, то исключения поднимать нет смысла, достаточно сформировать сообщение пользователю и показать его.
Здравствуйте, mrjeka, Вы писали:
M>Вот например в вашем ответе есть недочет. Я согласен с тем, что надо поднимать исключения наверх. Но в методе существуют еще и свои условия, по которым выполнение операции невозможно. Если это метод библиотеки, то в этой ситуации надо поднимать (кастомное) исключение, которого нет в вашем примере. Если это метод UI, то исключения поднимать нет смысла, достаточно сформировать сообщение пользователю и показать его.
Ну опять таки это имеет смысл если унифицировать интерфейс, а пока такого нет лучше кидать исключения как есть.
Не в обиду ТСу, но мне кажется так заморачиваться по поводу Exception сверх расточительство личного времени.
Причины? Есть небольшая, давайте вспомним, когда это реально необходимо (и то частично), на примере более краткой, неточной схеме разработки систем.
Схема:
1) Пишем, стуча по клаве код, который как-то отражает поставленное ТЗ, т.е. в конечном счете задача решена;
2) Рефакторим, use паттерн's, оптимизируем и тому подобное;
3) Пишем тесты, тестируем! Пишем тесты, тестируем и так до релиза системы; (надеюсь всем понятно, что после каждого теста идет исправление ошибок и отлавливания каких либо исключающих ситуаций)
4) Распространяем приложение и ждем от пользователей гневных сообщений о том, что не работает
Повторяем пункт 3, а иногда и 2.
Реальный пример: здесь
Просто история версий. Надеюсь всем понятно для чего я её привел.
P.S. Это конечно не идеал и не хочется всегда так разрабатывать системы, но это по моему мнению реальность.
P.S.S. Наверно открыл глаза ТС'y =)
P.S.S.S. Не ставьте минусы плиз, просто личное мнение, никому не навязываю.
Здравствуйте, 0K, Вы писали:
0K>Есть конкретные требования, которые вы не выполнили. Из чего я делаю вывод -- что вы просто приходите сюда пофлеймить. С такими людьми как вы работать -- равносильно плоному провалу. Делать ничгео не умеете, всех критикуете, времени на вас уходит уйма.
0K>Вам задача ясна, что было видно из вашей критики других участников. А придираться и требовать схем, более четких и непротиворечивых объяснений -- это участь леньтяев и неудачников, которые реально ничего делать не умеют.
А ты приходишь сюда поиграть в биг босса, я так понимаю?
Для порядка размещу и свой вариант. В конкурсе я специально не стал учавствовать (конкурс закончился сегодня 1 сентября) из-за неадекватного поведения некоторых товарищей.
Собственно вариант и несколько комментариев:
using System;
using System.Globalization;
using System.IO;
using System.Runtime.Serialization;
using System.Security;
namespace ConsoleApplication1
{
[Serializable]
public class CounterException : Exception
{
public CounterException() { }
public CounterException(string message) : base(message) { }
public CounterException(string message, Exception innerException) : base(message, innerException) { }
protected CounterException(SerializationInfo info, StreamingContext context)
: base(info, context) { }
}
public class Counter
{
private string _path;
public string Alias { get; protected set; }
public int Count { get; protected set; }
protected Counter() { }
/// <exception cref="ArgumentNullException">alias is null.</exception>
/// <exception cref="ArgumentOutOfRangeException">alias is empty</exception>
/// <exception cref="ArgumentException">Invalid character</exception>public Counter(string alias)
{
if (null == alias)
throw new ArgumentNullException("alias");
if (string.IsNullOrEmpty(alias))
throw new ArgumentOutOfRangeException("alias");
if (alias.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0)
throw new ArgumentException("Invalid character.");
Alias = alias;
_path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Alias);
}
public static Counter TryParse(string alias)
{
try { return new Counter(alias); }
catch (ArgumentException)
{
return null;
}
}
/// <exception cref="CounterException"></exception>public void Load()
{
int count;
if (File.Exists(_path))
{
try
{
string text = File.ReadAllText(_path);
if (!int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture.NumberFormat, out count))
count = 0;
}
catch (PathTooLongException exception)
{
throw new CounterException(exception.Message, exception);
}
catch (UnauthorizedAccessException exception)
{
throw new CounterException(exception.Message, exception);
}
catch (FileNotFoundException)
{
count = 0;
}
catch (SecurityException exception)
{
throw new CounterException(exception.Message, exception);
}
catch (IOException exception)
{
throw new CounterException(exception.Message, exception);
}
}
else
count = 0;
Count = count;
}
public void Increment()
{
Count++;
}
/// <exception cref="CounterException"></exception>public void Save()
{
try
{
File.WriteAllText(_path, Count.ToString(CultureInfo.InvariantCulture.NumberFormat));
}
catch (PathTooLongException exception)
{
throw new CounterException(exception.Message, exception);
}
catch (UnauthorizedAccessException exception)
{
throw new CounterException(exception.Message, exception);
}
catch (FileNotFoundException exception)
{
throw new CounterException(exception.Message, exception);
}
catch (SecurityException exception)
{
throw new CounterException(exception.Message, exception);
}
catch (IOException exception)
{
throw new CounterException(exception.Message, exception);
}
}
}
public class Program
{
static void Main(string[] args)
{
const string YES_SYMBOLS = "YyДд";
bool first = true;
Counter counter;
while (true)
{
if (!first)
{
Console.WriteLine("Повторить?");
if (!YES_SYMBOLS.Contains(Console.ReadLine()))
break;
Console.Clear();
}
else
first = false;
Console.WriteLine("Введите имя счетчика:");
string alias = Console.ReadLine();
counter = Counter.TryParse(alias);
if (null == counter)
{
Console.WriteLine("Некорректное имя счетчика (пояснение каким должно быть правильное имя).");
continue;
}
try
{
counter.Load();
}
catch (CounterException exception)
{
Console.WriteLine("Ошибка при получении значения счетчика." + Environment.NewLine + exception.Message);
continue;
}
Console.WriteLine("Текущее значение счетчика {0}: {1}.", counter.Alias, counter.Count);
counter.Increment();
try
{
counter.Save();
}
catch (CounterException exception)
{
Console.WriteLine("Ошибка при сохранении нового значения счетчика." + Environment.NewLine + exception.Message);
continue;
}
}
}
}
}
Главная загвоздка в данном задании -- целая гармошка возможных исключений при работе с файлами. И по идее (которую даже сами MS не выполняют)
-- нельзя использовать Exception. Я решил так и оставить эту гармошку (5 исключений) в 2-х местах, т.к. в .Net нет вменяемых средств для устранения дублирования и повышения читаемости такого кода.
Весь смысл кода -- обернуть эту гармошку в одно простое исключение, ясно отражающее причину ошибки и содержащее всю необходимую информацию.
Если кто-то сможет покритиковать по делу или добавить свой вариант -- буду благодарен.
Т.к. конкурс многими не был воспринят адекватно -- итоги подводить большого смысла нет (да и не представляется возможным, т.к. голосование с выборами победителя куда-то пропало: либо было удалено модератором, либо по техническим причинам).
Вообще-то тема создавалась для тех кто больше всех возмущался по поводу обработки исключений -- чтобы посмотреть на что они способны практически. Как и следовало ожидать -- и в этой теме от них ничего выбить не удалось (кроме тех же возмущений, но уже по поводу неправильно задания).
Здравствуйте, 0K, Вы писали:
0K>Вообще-то тема создавалась для тех кто больше всех возмущался по поводу обработки исключений -- чтобы посмотреть на что они способны практически. Как и следовало ожидать -- и в этой теме от них ничего выбить не удалось (кроме тех же возмущений, но уже по поводу неправильно задания).
Утверждалось, что есть два типа исключений: для программистов и для пользователей. И ставился вопрос: почему они никак не различаются (ни с помощью наследования, ни еще какими-нибудиь средствами)?
Большинство справедливо возразило: исключения _только_ для программистов. И это программисты могут (не должны!) пользоваться exception.message для передачи user-friendly отрицательных результатов. Все таки exception это такая же часть контракта, как и типы возвращаемых значений. Ты же не собираешься публиковать "парадигму типов возврата и обработки возвращаемых значений"?
Теперь смотрим на твою реализацию:
где видно разделение на UserFriendly и NonUserFriendly Exception?
где обязательно-принудительный вывод текста UserFriendly Exception?
зато прекрасно видно, что есть решения программиста: поймал _ожидаемое_ исключение, обработал его там где смог и как захотел. Захотел — вывел строчку пользователя, захотел — установил счетчик в 0.
Делается вывод: разделение на UserFriendly Exception и NonUserFriendly в твоем примере не нужно, либо не нужно вообще.
0K>Надеюсь тема была полезной не только мне.
Касательно самого примера: из _интерактивных_ консольных приложений, кроме osql(он для пользователей или программистов?) и vi(кто-нибудь видел какие exception.message он выводит?), я помню только текстовые adventures и mud-ы годов этак 80-x... Сейчас консольные приложения либо выполняют запрошеное действие либо возвращают ненулевой код возврата, с выводом сообщения в error stream, что позволяет из них делать высокоуровневые композиции в shell/batch скриптах.
Здравствуйте, 0K, Вы писали:
0K>Собственно вариант и несколько комментариев:
Хотел бы в свою очередь высказать несколько замечаний/пожеланий:
— первое что бросается в глаза — очень неявный код presentation layer'а. Т.е. в нём основное, что видно — танцы вокруг исключений, а никак не собственно работа. Причём это в принципе касается не только presentation'а — вы вообще мало где пишете больше чем две строчки эфективного кода подряд. Это очень тяжело читать. На продакшн код мало похоже.
— текст исключения (например "Ошибка при получении значения счетчика") вы храните в классе-пользователе. Это в принципе ставит крест на том, чтобы делать без оборачивания больше, чем одно действие. Хотелось бы в связи с этим взглянуть на код, в котором класс Counter будет использоваться хотя бы раза три (в разных местах).
— также хотелось бы посмотреть на более развитую иерархию классов — как работать в таком случае? Предположим между консолью и Counter'ом вписался некий класс, ответсвенность которого просто выдавать уникальное увеличивающееся значение. Как будет выглядеть код этих трёх классов?
— вы обернули все методы кроме Increment. Почему так? Смею предположить, что это из-за того, что в аннотациях у него сказано, что он может что-либо выбрасывать. А как это может жить в развивающемся коде? Ведь невозможно помнить и обновлять все места, где используется некий метод.
0K>Весь смысл кода -- обернуть эту гармошку в одно простое исключение, ясно отражающее причину ошибки и содержащее всю необходимую информацию.
гм. а не могли бы вы показать, где вы выполняете "оборачивание в одно просто исключение"? а то я вижу как гармошка разворачивается, а где сворачивается не могу понять.
Здравствуйте, 0K, Вы писали:
0K>Главная загвоздка в данном задании -- целая гармошка возможных исключений при работе с файлами. И по идее (которую даже сами MS не выполняют) 0K> -- нельзя использовать Exception. Я решил так и оставить эту гармошку (5 исключений) в 2-х местах, т.к. в .Net нет вменяемых средств для устранения дублирования и повышения читаемости такого кода.
В The Exception Handling Application Block можно подключить общую Wrap Policy к каждому типу исключения из «гармошки». Да и вручную тоже несложно — создай список Type'ов рассматриваемых исключений и в catch-блоке смотри на принадлежность typeof(ex) этому списку. Или словарик, ключом которого будет тип исключения, значением — обработчик (почти везде один и тот же — Wrap, кроме FileNotFoundException, где count = 0).
Кроме того, на исключения, текст сообщений которых заведомо можно выводить пользователю (как правило, они выбрасываются уже в Presentation Layer'е; в твоём коде он перемешан с бизнес-логикой), тоже можно повесить политику — Notify User Policy.
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, 0K, Вы писали:
0K>Главная загвоздка в данном задании -- целая гармошка возможных исключений при работе с файлами. И по идее (которую даже сами MS не выполняют)
Главная загвоздка в задании в том что ты списал требования со своего решения. Потому все комментарии в плоскости соответствия кода решаемой задачи бессмысленны.
0K> -- нельзя использовать Exception. Я решил так и оставить эту гармошку (5 исключений) в 2-х местах, т.к. в .Net нет вменяемых средств для устранения дублирования и повышения читаемости такого кода.
Видимо претензия к дотнету заключается в отсутствии классификации исключений, заточенную под твое решение этой задачи.
0K>Весь смысл кода -- обернуть эту гармошку в одно простое исключение, ясно отражающее причину ошибки и содержащее всю необходимую информацию.
От того что ты обернул исключение своим, причина ошибки яснее не стала и информации не добавилось. Единственное что ты достиг оборачиванием — сгруппировал интересующие тебя типы исключения в единый. А это можно было достичь и без гармошек.
Еще твои гармошки можно сократить если внимательно почитать документацию к File.Exists.
0K>Если кто-то сможет покритиковать по делу или добавить свой вариант -- буду благодарен.
Куда уж кому-то смочь покритиковать по делу тебя самого! По делу тут только ты, остальные тут лишь выгодная декорация твоему триумфу
З.Ы. Хотелось бы посмотреть на твой код, если бы у тебя был инструментарий с "правильной" парадигмой, дабы тугоумие MS не оттеняло его.
Здравствуйте, samius, Вы писали:
S>От того что ты обернул исключение своим, причина ошибки яснее не стала и информации не добавилось. Единственное что ты достиг оборачиванием — сгруппировал интересующие тебя типы исключения в единый. А это можно было достичь и без гармошек.
кстати, да — конструкция throw new CounterException(exception.Message, exception); ничего не добавляет по сути. причём странновастый момент в том, что в стеке ошибок exception.Message будет повторен дважды.
И опять же — ответственность по выдаче текста, который реально может помочь при решении проблемы лежит в классе-пользователе. Т.е. класс Counter рассчитывает на то, что его будут использовать определённым способом. А если нет — то ошибка будет неочевидна. Кстати, мелкомягкий код этим не страдает (если уж брать его за эталон).
всю ночь не ем, весь день не сплю — устаю
Re[3]: Подгонка формулировки задачи под требуемый результат
Здравствуйте, samius, Вы писали:
S>Главная загвоздка в задании в том что ты списал требования со своего решения. Потому все комментарии в плоскости соответствия кода решаемой задачи бессмысленны.
«Вы можете выбрать любой цвет, при условии, что он — чёрный. Ага, вам пришлось выбрать чёрный — я же говорил!»
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, 0K, Вы писали:
0K>Для порядка размещу и свой вариант. В конкурсе я специально не стал учавствовать (конкурс закончился сегодня 1 сентября) из-за неадекватного поведения некоторых товарищей.
0K>Собственно вариант и несколько комментариев: [skipped]
Да уж, на одну строку логики 3 строки непонятно чего. Кроме того нету обработки переполнения, и немалый security баг.
Вообще код выглядит как большая ошибка проектирования.
0K>Главная загвоздка в данном задании -- целая гармошка возможных исключений при работе с файлами. И по идее (которую даже сами MS не выполняют) 0K> -- нельзя использовать Exception. Я решил так и оставить эту гармошку (5 исключений) в 2-х местах, т.к. в .Net нет вменяемых средств для устранения дублирования и повышения читаемости такого кода.
Ты наверное не понимаешь что большинство исключений для обычной программы являются критическими, а пользователи не вводят имена файлов.
0K>Весь смысл кода -- обернуть эту гармошку в одно простое исключение, ясно отражающее причину ошибки и содержащее всю необходимую информацию.
И для этого ты два раза скопипастил код обработки? Браво!
0K>Если кто-то сможет покритиковать по делу или добавить свой вариант -- буду благодарен.
Упрощу немного твой вариант:
using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Security;
using System.Collections.Generic;
namespace ConsoleApplication1
{
public class Counter
{
private string _path;
public string Alias { get; protected set; }
public int Count { get; protected set; }
public Counter(string alias)
{
if (null == alias)
throw new ArgumentNullException("alias");
if (string.IsNullOrEmpty(alias))
throw new ArgumentOutOfRangeException("alias");
if (alias.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0)
throw new ArgumentException("Invalid character.");
Alias = alias;
_path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Alias);
}
public void Load()
{
try
{
string text = File.ReadAllText(_path);
int count = 0;
int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture.NumberFormat, out count);
Count = count;
}
catch (FileNotFoundException)
{
Count = 0;
}
}
public void Increment()
{
Count++;
}
public void Save()
{
File.WriteAllText(_path, Count.ToString(CultureInfo.InvariantCulture.NumberFormat));
}
}
public class Program
{
static bool HandleException(Action action, IEnumerable<Type> exceptionsToHandle, Action<Exception> handler)
{
try
{
action();
return true;
}
catch (Exception e)
{
if (exceptionsToHandle.Any(t => t.IsAssignableFrom(e.GetType())))
{
handler(e);
return false;
}
throw;
}
}
static void Main(string[] args)
{
const string YES_SYMBOLS = "YyДд";
var exeptionTypes = new []
{ typeof(PathTooLongException),
typeof(UnauthorizedAccessException),
typeof(FileNotFoundException),
typeof(SecurityException),
typeof(IOException),
};
while (true)
{
Console.WriteLine("Введите имя счетчика:");
string alias = Console.ReadLine();
try
{
var counter = new Counter(alias);
if(HandleException(counter.Load, exeptionTypes,
e => Console.WriteLine("Ошибка при получении значения счетчика." + Environment.NewLine + e.Message)))
{
Console.WriteLine("Текущее значение счетчика {0}: {1}.", counter.Alias, counter.Count);
counter.Increment();
HandleException(counter.Save, exeptionTypes,
e => Console.WriteLine("Ошибка при сохранении нового значения счетчика." + Environment.NewLine + e.Message)))
}
}
catch (ArgumentException)
{
Console.WriteLine("Некорректное имя счетчика (пояснение каким должно быть правильное имя).");
}
Console.WriteLine("Повторить?");
if (!YES_SYMBOLS.Contains(Console.ReadLine()))
break;
Console.Clear();
}
}
}
}
В итоге CounterException не нужен, потом что его роль в исходной программе — только передача сообщения. Это вполне можно делать более простыми способами. О чем я тебе и говорил все время.
Переполнение счетчика и security баг оставлю на твоей совести.
Здравствуйте, Qbit86, Вы писали:
Q>Здравствуйте, 0K, Вы писали:
0K>>Главная загвоздка в данном задании -- целая гармошка возможных исключений при работе с файлами. И по идее (которую даже сами MS не выполняют) 0K>> -- нельзя использовать Exception. Я решил так и оставить эту гармошку (5 исключений) в 2-х местах, т.к. в .Net нет вменяемых средств для устранения дублирования и повышения читаемости такого кода.
Q>В The Exception Handling Application Block можно подключить общую Wrap Policy к каждому типу исключения из «гармошки». Да и вручную тоже несложно — создай список Type'ов рассматриваемых исключений и в catch-блоке смотри на принадлежность typeof(ex) этому списку. Или словарик, ключом которого будет тип исключения, значением — обработчик (почти везде один и тот же — Wrap, кроме FileNotFoundException, где count = 0).
Q>Кроме того, на исключения, текст сообщений которых заведомо можно выводить пользователю (как правило, они выбрасываются уже в Presentation Layer'е; в твоём коде он перемешан с бизнес-логикой), тоже можно повесить политику — Notify User Policy.
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, 0K, Вы писали:
0K>Для порядка размещу и свой вариант. В конкурсе я специально не стал учавствовать (конкурс закончился сегодня 1 сентября) из-за неадекватного поведения некоторых товарищей.
0K>Собственно вариант и несколько комментариев:
0K>
0K>using System;
0K>using System.Globalization;
0K>using System.IO;
0K>using System.Runtime.Serialization;
0K>using System.Security;
0K>namespace ConsoleApplication1
0K>{
0K> [Serializable]
0K> public class CounterException : Exception
0K> {
0K> public CounterException() { }
0K> public CounterException(string message) : base(message) { }
0K> public CounterException(string message, Exception innerException) : base(message, innerException) { }
0K> protected CounterException(SerializationInfo info, StreamingContext context)
0K> : base(info, context) { }
0K> }
0K> public class Counter
0K> {
0K> private string _path;
0K> public string Alias { get; protected set; }
0K> public int Count { get; protected set; }
0K> protected Counter() { }
0K> /// <exception cref="ArgumentNullException">alias is null.</exception>
0K> /// <exception cref="ArgumentOutOfRangeException">alias is empty</exception>
0K> /// <exception cref="ArgumentException">Invalid character</exception>
0K> public Counter(string alias)
0K> {
0K> if (null == alias)
0K> throw new ArgumentNullException("alias");
0K> if (string.IsNullOrEmpty(alias))
0K> throw new ArgumentOutOfRangeException("alias");
0K> if (alias.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0)
0K> throw new ArgumentException("Invalid character.");
0K> Alias = alias;
0K> _path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Alias);
0K> }
0K> public static Counter TryParse(string alias)
0K> {
0K> try { return new Counter(alias); }
0K> catch (ArgumentException)
0K> {
0K> return null;
0K> }
0K> }
0K> /// <exception cref="CounterException"></exception>
0K> public void Load()
0K> {
0K> int count;
0K> if (File.Exists(_path))
0K> {
0K> try
0K> {
0K> string text = File.ReadAllText(_path);
0K> if (!int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture.NumberFormat, out count))
0K> count = 0;
0K> }
0K> catch (PathTooLongException exception)
0K> {
0K> throw new CounterException(exception.Message, exception);
0K> }
0K> catch (UnauthorizedAccessException exception)
0K> {
0K> throw new CounterException(exception.Message, exception);
0K> }
0K> catch (FileNotFoundException)
0K> {
0K> count = 0;
0K> }
0K> catch (SecurityException exception)
0K> {
0K> throw new CounterException(exception.Message, exception);
0K> }
0K> catch (IOException exception)
0K> {
0K> throw new CounterException(exception.Message, exception);
0K> }
0K> }
0K> else
0K> count = 0;
0K> Count = count;
0K> }
0K> public void Increment()
0K> {
0K> Count++;
0K> }
0K> /// <exception cref="CounterException"></exception>
0K> public void Save()
0K> {
0K> try
0K> {
0K> File.WriteAllText(_path, Count.ToString(CultureInfo.InvariantCulture.NumberFormat));
0K> }
0K> catch (PathTooLongException exception)
0K> {
0K> throw new CounterException(exception.Message, exception);
0K> }
0K> catch (UnauthorizedAccessException exception)
0K> {
0K> throw new CounterException(exception.Message, exception);
0K> }
0K> catch (FileNotFoundException exception)
0K> {
0K> throw new CounterException(exception.Message, exception);
0K> }
0K> catch (SecurityException exception)
0K> {
0K> throw new CounterException(exception.Message, exception);
0K> }
0K> catch (IOException exception)
0K> {
0K> throw new CounterException(exception.Message, exception);
0K> }
0K> }
0K> }
0K> public class Program
0K> {
0K> static void Main(string[] args)
0K> {
0K> const string YES_SYMBOLS = "YyДд";
0K> bool first = true;
0K> Counter counter;
0K> while (true)
0K> {
0K> if (!first)
0K> {
0K> Console.WriteLine("Повторить?");
0K> if (!YES_SYMBOLS.Contains(Console.ReadLine()))
0K> break;
0K> Console.Clear();
0K> }
0K> else
0K> first = false;
0K> Console.WriteLine("Введите имя счетчика:");
0K> string alias = Console.ReadLine();
0K> counter = Counter.TryParse(alias);
0K> if (null == counter)
0K> {
0K> Console.WriteLine("Некорректное имя счетчика (пояснение каким должно быть правильное имя).");
0K> continue;
0K> }
0K> try
0K> {
0K> counter.Load();
0K> }
0K> catch (CounterException exception)
0K> {
0K> Console.WriteLine("Ошибка при получении значения счетчика." + Environment.NewLine + exception.Message);
0K> continue;
0K> }
0K> Console.WriteLine("Текущее значение счетчика {0}: {1}.", counter.Alias, counter.Count);
0K> counter.Increment();
0K> try
0K> {
0K> counter.Save();
0K> }
0K> catch (CounterException exception)
0K> {
0K> Console.WriteLine("Ошибка при сохранении нового значения счетчика." + Environment.NewLine + exception.Message);
0K> continue;
0K> }
0K> }
0K> }
0K> }
0K>}
0K>
0K>Главная загвоздка в данном задании -- целая гармошка возможных исключений при работе с файлами. И по идее (которую даже сами MS не выполняют) 0K> -- нельзя использовать Exception. Я решил так и оставить эту гармошку (5 исключений) в 2-х местах, т.к. в .Net нет вменяемых средств для устранения дублирования и повышения читаемости такого кода.
0K>Весь смысл кода -- обернуть эту гармошку в одно простое исключение, ясно отражающее причину ошибки и содержащее всю необходимую информацию.
0K>Если кто-то сможет покритиковать по делу или добавить свой вариант -- буду благодарен.
Про поведение:
1. После инкремента значения 2147483647 в файле оказывается отрицательно число.
2. При указании имени счетчика ".." пишет "Текущее значение счетчика ..: 0"
Я понимаю, что ТЗ ваше, и, наверное, так требовалось по ТЗ.
Про код:
1. Зачем метод Counter.TryParse, если вы на том же уровне (из Program.Main) делаете обернутый counter.Load и counter.Save? Что мешало сделать такой же обернутый new Counter(alias)? Как-то непоследовательно...
2. Не описана ответственность класса Counter. Можно-ли дергать за .Increment() до .Load() ? Можно-ли дергать за .Increment() после .Save()? Тут явное нарушение SRP: класс отвечает как за инкремнтирование счетчика, так и за работу с файловым хранилищем.
3. Counter.Increment() гаранитрует успешеное выполнение ?
4. Console.ReadLine() тоже может бросать исключения.
Здравствуйте, Аноним, Вы писали:
А>Большинство справедливо возразило: исключения _только_ для программистов.
Большинство, как правило, ошибается. Дураков большинство.
А>И это программисты могут (не должны!) пользоваться exception.message для передачи user-friendly отрицательных результатов.
Не всех.
А>Все таки exception это такая же часть контракта, как и типы возвращаемых значений. Ты же не собираешься публиковать "парадигму типов возврата и обработки возвращаемых значений"?
Exception -- не только контракт. Message контрактных исключений НИКОГДА не нужно выводить пользователю -- они только для программиста. Есть бизнес-исключения -- вот они содержат полезый для пользователя Message.
А>Теперь смотрим на твою реализацию: А>где видно разделение на UserFriendly и NonUserFriendly Exception?
UserFriendly -- это CounterException. Его Message полезен пользователю. Хотя там можно чуть красивее сделать с Message, возможно покажу.
А>где обязательно-принудительный вывод текста UserFriendly Exception?
А разве не видно? Везде при возникновении ошибки выводится Message.
А>зато прекрасно видно, что есть решения программиста: поймал _ожидаемое_ исключение, обработал его там где смог и как захотел. Захотел — вывел строчку пользователя, захотел — установил счетчик в 0.
CounterException только для вывода пользователю используется.
А>Касательно самого примера: из _интерактивных_ консольных приложений, кроме osql(он для пользователей или программистов?) и vi(кто-нибудь видел какие exception.message он выводит?), я помню только текстовые adventures и mud-ы годов этак 80-x... Сейчас консольные приложения либо выполняют запрошеное действие либо возвращают ненулевой код возврата, с выводом сообщения в error stream, что позволяет из них делать высокоуровневые композиции в shell/batch скриптах.
Здравствуйте, Neco, Вы писали:
N>- первое что бросается в глаза — очень неявный код presentation layer'а. Т.е. в нём основное, что видно — танцы вокруг исключений, а никак не собственно работа. Причём это в принципе касается не только presentation'а — вы вообще мало где пишете больше чем две строчки эфективного кода подряд. Это очень тяжело читать. На продакшн код мало похоже.
Ну а как-же? Предлагаете ваш вариант с делегатами использовать (это у вас было?)? Это слишком запутано. А если я изменю на Win или Web-приложение? Гораздо удобнее отловить мое UserFriendly Exception и вывести информацию пользователю в удобном виде.
N>- текст исключения (например "Ошибка при получении значения счетчика") вы храните в классе-пользователе. Это в принципе ставит крест на том, чтобы делать без оборачивания больше, чем одно действие. Хотелось бы в связи с этим взглянуть на код, в котором класс Counter будет использоваться хотя бы раза три (в разных местах).
Вот за это спасибо. Я уже когда спать вчера лег -- как раз об этом подумал. Правильное замечание -- у меня получилось дублирование кода.
N>- также хотелось бы посмотреть на более развитую иерархию классов — как работать в таком случае? Предположим между консолью и Counter'ом вписался некий класс, ответсвенность которого просто выдавать уникальное увеличивающееся значение. Как будет выглядеть код этих трёх классов?
Собственно в чем проблема? Можно сделать наследника Counter -- там конструктор даже без параметров есть.
N>- вы обернули все методы кроме Increment. Почему так? Смею предположить, что это из-за того, что в аннотациях у него сказано, что он может что-либо выбрасывать. А как это может жить в развивающемся коде? Ведь невозможно помнить и обновлять все места, где используется некий метод.
Помнить не возможно, есть инструменты для проведения рефакторинга. Другого способа нет -- если изменились контракты -- значит должен измениться и вызывающий код.
0K>>Весь смысл кода -- обернуть эту гармошку в одно простое исключение, ясно отражающее причину ошибки и содержащее всю необходимую информацию. N>гм. а не могли бы вы показать, где вы выполняете "оборачивание в одно просто исключение"? а то я вижу как гармошка разворачивается, а где сворачивается не могу понять.
Сворачивается в 2-х местах и получается CounterException.
Здравствуйте, Qbit86, Вы писали:
Q>В The Exception Handling Application Block можно подключить общую Wrap Policy к каждому типу исключения из «гармошки». Да и вручную тоже несложно — создай список Type'ов рассматриваемых исключений и в catch-блоке смотри на принадлежность typeof(ex) этому списку. Или словарик, ключом которого будет тип исключения, значением — обработчик (почти везде один и тот же — Wrap, кроме FileNotFoundException, где count = 0).
Это все не красиво. Хотя на счет словаря была идея. Возможно в новой версии применю словарь.
Здравствуйте, samius, Вы писали:
0K>>Главная загвоздка в данном задании -- целая гармошка возможных исключений при работе с файлами. И по идее (которую даже сами MS не выполняют) S>Главная загвоздка в задании в том что ты списал требования со своего решения. Потому все комментарии в плоскости соответствия кода решаемой задачи бессмысленны.
Свое решение наваял вчера на сонную голову.
0K>> -- нельзя использовать Exception. Я решил так и оставить эту гармошку (5 исключений) в 2-х местах, т.к. в .Net нет вменяемых средств для устранения дублирования и повышения читаемости такого кода. S>Видимо претензия к дотнету заключается в отсутствии классификации исключений, заточенную под твое решение этой задачи.
Не только этой задачи. Всех задач.
0K>>Весь смысл кода -- обернуть эту гармошку в одно простое исключение, ясно отражающее причину ошибки и содержащее всю необходимую информацию. S>От того что ты обернул исключение своим, причина ошибки яснее не стала и информации не добавилось. Единственное что ты достиг оборачиванием — сгруппировал интересующие тебя типы исключения в единый. А это можно было достичь и без гармошек.
Да, в этом ошибся. Нужно было добавить инфы.
S>Еще твои гармошки можно сократить если внимательно почитать документацию к File.Exists.
Его вообще нужно убрать. Там перехват Exception, что нарушает правила.
Здравствуйте, Neco, Вы писали:
N>кстати, да — конструкция throw new CounterException(exception.Message, exception); ничего не добавляет по сути. причём странновастый момент в том, что в стеке ошибок exception.Message будет повторен дважды.
Да, там нужно добавить инфы, ошибся на сонную голову. Выше уже отписал.
N>И опять же — ответственность по выдаче текста, который реально может помочь при решении проблемы лежит в классе-пользователе.
Вот это нужно исправить.
N>Т.е. класс Counter рассчитывает на то, что его будут использовать определённым способом. А если нет — то ошибка будет неочевидна. Кстати, мелкомягкий код этим не страдает (если уж брать его за эталон).
В данном случае нет смысла требовать последовательности вызовов Load Increment Save. Можно что-то пропустить --- это дело вызывающего кода. А вот сообщения в класс Counter добавить нужно, иначе будет дублирование кода.
Здравствуйте, gandjustas, Вы писали:
0K>>Собственно вариант и несколько комментариев: [skipped] G>Да уж, на одну строку логики 3 строки непонятно чего.
"Непонятно чего" -- вы называете обработку исключения?
G>Кроме того нету обработки переполнения, и немалый security баг.
Это не критично -- оговаривалось. В данном задание такое поведение является нормальным, т.к. главное что требуется -- корректно обработать исключения и выдать корректные сообщения.
Вы, кстати, так и не смогли этого сделать.
G>Вообще код выглядит как большая ошибка проектирования.
Вы вообще не смогли ничего сделать. Так что помолчали бы. Чтобы так сказать -- нужно самому сделать правильно и показать как правильно.
G>Ты наверное не понимаешь что большинство исключений для обычной программы являются критическими,
Для разных программ соотношение критические/не критические может быть разным. И что?
G>а пользователи не вводят имена файлов.
А кто вводит?
0K>>Весь смысл кода -- обернуть эту гармошку в одно простое исключение, ясно отражающее причину ошибки и содержащее всю необходимую информацию. G>И для этого ты два раза скопипастил код обработки? Браво!
Не внимательно смотрели. Разница есть. Но там еще нужно добавить сообщение, разное для каждого из 2-х случаев.
G>Упрощу немного твой вариант:
Вы не упростили:
1. при загрузке неверно обработали FileNotFoundException
2. перенос обработки в функцию принимающую делегаты в качестве параметров -- только усложнил восприятие. А что если код будет использовать в Win-приложении и при возникновении ошибки нужно будет некоторые элементы сделать видимыми/другие неактивными? Тогда вы введете 2 дополнительные мелкие функции, а этого лучше не делать.
Ну и зря выкинули TryParse -- с ним меньше блоков и гораздо понятнее код.
А основную мою ошибку вы так и не исправили.
G>В итоге CounterException не нужен, потом что его роль в исходной программе — только передача сообщения. Это вполне можно делать более простыми способами. О чем я тебе и говорил все время.
Но ваш вызывающий код должен знать логику работы -- какие исключения могут возникнуть. И если вы измените тип хранилища на базу данных -- нужно будет переделывать вызывающий код. Вот так то. Об этом у вас не хватило подумать.
Здравствуйте, xpalex, Вы писали:
X>1. Зачем метод Counter.TryParse, если вы на том же уровне (из Program.Main) делаете обернутый counter.Load и counter.Save? Что мешало сделать такой же обернутый new Counter(alias)? Как-то непоследовательно...
Т.к. Counter.TryParse -- не возвращает дополнительной информации. В случае с сохранением/загрузкой -- может быть слишком много вариантов проблемы и все их нужно знать.
X>2. Не описана ответственность класса Counter. Можно-ли дергать за .Increment() до .Load() ? Можно-ли дергать за .Increment() после .Save()? Тут явное нарушение SRP: класс отвечает как за инкремнтирование счетчика, так и за работу с файловым хранилищем.
Вообще-то идея разделить ответственность у меня возникала, но для такой простой задачи -- лишнее. В данном случае можно дергать методы без всяких ограничений -- нарушения не будет, это дело вызывающего кода (если ему так нужно -- пусть дергает сначала Save, потом Increment, потом опять Save).
X>3. Counter.Increment() гаранитрует успешеное выполнение ?
Здравствуйте, Uzzy, Вы писали:
U>Зачем повторная проверка на null ?
Это не повторная проверка на null. Это независимая проверка. Проверка того, что входные данные уже были подготовлены, контракты соблюдены.
Эти проверки в общем случае осуществляются разными людьми — автором библиотеки и пользователем библиотеки. По указанной выше ссылке — подробнее. (Хотя конкретно в приведённом коде я двойной проверки не вижу.)
Здравствуйте, midcyber, Вы писали:
M>Неужто глаз уже так замылился? =) G>>> if (null == alias) G>>> if (string.IsNullOrEmpty(alias))
А, та это ничего страшного, просто вызвал товарищ стандартную функцию. Эта лишняя проверка лучше, имхо, чем сравнение Lenth с нулём, на которую FxCop (CodeAnalysis) к тому же ругается. Можно, конечно, как-нибудь !alias.Any(), но овчинка не стоит выделки.
Алсо, там могло быть ещё и третье сравнение с null'ом, типа String.IsNullOrWhitespace (фишка FCL 4.0).
Здравствуйте, Qbit86, Вы писали:
Q>Здравствуйте, midcyber, Вы писали:
M>>Неужто глаз уже так замылился? =) G>>>> if (null == alias) G>>>> if (string.IsNullOrEmpty(alias))
Q>А, та это ничего страшного,
Ну а теперь предположим, что в процессе разработки эти проверки перепутают местами
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, gandjustas, Вы писали:
0K>>>Собственно вариант и несколько комментариев: [skipped] G>>Да уж, на одну строку логики 3 строки непонятно чего.
0K>"Непонятно чего" -- вы называете обработку исключения?
G>>Кроме того нету обработки переполнения, и немалый security баг.
0K>Это не критично -- оговаривалось. В данном задание такое поведение является нормальным, т.к. главное что требуется -- корректно обработать исключения и выдать корректные сообщения.
Кто ты понимаешь по корректной обработкой исключения и под выдачей корректного сообщения?
Ты же кстати не сделал локализацию сообщений
0K>Вы, кстати, так и не смогли этого сделать.
Выдаются в точности те сообщения что у тебя
G>>Вообще код выглядит как большая ошибка проектирования. 0K>Вы вообще не смогли ничего сделать. Так что помолчали бы. Чтобы так сказать -- нужно самому сделать правильно и показать как правильно.
Повторенное дважды правдой не становится, я привел два варианта кода с разными usecase.
G>>а пользователи не вводят имена файлов. 0K> А кто вводит?
Администратор в конфиге. Пользователь может выбрать существующий файл в диалоге.
0K>>>Весь смысл кода -- обернуть эту гармошку в одно простое исключение, ясно отражающее причину ошибки и содержащее всю необходимую информацию. G>>И для этого ты два раза скопипастил код обработки? Браво!
0K>Не внимательно смотрели. Разница есть. Но там еще нужно добавить сообщение, разное для каждого из 2-х случаев.
Нету там разницы, везде
throw new CounterException(exception.Message, exception);
G>>Упрощу немного твой вариант:
0K>Вы не упростили:
0K>1. при загрузке неверно обработали FileNotFoundException
Да ну? И чем же неверно?
0K>2. перенос обработки в функцию принимающую делегаты в качестве параметров -- только усложнил восприятие.
А для меня упростил.
0K>А что если код будет использовать в Win-приложении и при возникновении ошибки нужно будет некоторые элементы сделать видимыми/другие неактивными?
Ну сделаю это в хендлерах.
0K>Тогда вы введете 2 дополнительные мелкие функции, а этого лучше не делать.
Счегобы? Это нарушает какие-то гайдлайны? Ты в курсе сколько гайдлайнов нарушает твой код?
0K>Ну и зря выкинули TryParse -- с ним меньше блоков и гораздо понятнее код.
Вообще надо проверку имени вынести отдельно и с помощью regexp, так сразу и security баг закроется.
0K>А основную мою ошибку вы так и не исправили.
Еще я ошибки в твоем коде искать буду... мне лениво.
Я просто показал что эквивалент твоему коду можно написать без эксепшена для передачи сообщения, при этом получилось в полтора раза короче.
А в реальной системе, при применении Exception Handling Application Block, можно всю такую обработку вынести вне логики.
G>>В итоге CounterException не нужен, потом что его роль в исходной программе — только передача сообщения. Это вполне можно делать более простыми способами. О чем я тебе и говорил все время.
0K>Но ваш вызывающий код должен знать логику работы -- какие исключения могут возникнуть.
Кому должен?
0K>И если вы измените тип хранилища на базу данных -- нужно будет переделывать вызывающий код.
Если я буду переделывать код, то я буду переделывать код вместе с обработкой ошибок.
Причем я это буду делать в одном месте, а тебе придется переделывать в двух.
0K>Вот так то. Об этом у вас не хватило подумать.
И правильно, надо решать задачу, которую надо решать, а не те которые могут возникнуть через 100 лет.
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, gandjustas, Вы писали:
G>>Это мне вопрос? Я скопипастил.
0K>Вы же переделали. НЕ пытайтесь избежать ответственности за свой код.
. По коду претензий не было, и тот и другой решает свои задачи.
Re[4]: Вместо подведения итога
От:
Аноним
Дата:
01.09.10 13:01
Оценка:
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, Аноним, Вы писали:
А>>Большинство справедливо возразило: исключения _только_ для программистов.
0K>Большинство, как правило, ошибается. Дураков большинство.
Т.к. конкурс многими не был воспринят адекватно
так бы и писали: Т.к. конкурс _дураками_ не был воспринят адекватно... Или вы не это хотели сказать?
А>>Все таки exception это такая же часть контракта, как и типы возвращаемых значений. Ты же не собираешься публиковать "парадигму типов возврата и обработки возвращаемых значений"?
0K>Exception -- не только контракт. Message контрактных исключений НИКОГДА не нужно выводить пользователю -- они только для программиста. Есть бизнес-исключения -- вот они содержат полезый для пользователя Message.
У каждого метода есть контракт: а) принимать определенные параметры б) выполнять набор действий в) возвращать определенный результат г) кидать одно из исключений при невозможности выполнить действие/вернуть результат
про особый вид "контрактных исключений" вы упоминаете впервые и ого! они тоже обладают уникальными свойствами: их message никогда! нельзя выводить пользователю. Следуя вашей категоризации у нас есть три типа exceptions:
контрактные — никогда нельзя выводить пользователю.
user-friendly — всегда выводить пользователю.
остальные.
0K>UserFriendly -- это CounterException. Его Message полезен пользователю. Хотя там можно чуть красивее сделать с Message, возможно покажу.
А>>где обязательно-принудительный вывод текста UserFriendly Exception?
0K>А разве не видно? Везде при возникновении ошибки выводится Message.
вариант 1: оно user-friendly потому что его message выводится пользователю? а если я выведу пользователю message от PathNotFoundException оно станет User-Friendly?
вариант 2: оно user-friendly поэтому его message всегда выводятся пользователю? (кстати можно или нужно?) и где тогда проверка, что пользователь получает сообщения только от user-friendly exception?
Вот потребуется продвинутый каунтер, который будет считатывать на сколько увеличивать счетчик из внешнего источника(например через веб-сервис) и при неудаче метод Counter.Increment() станет кидать CounterException — ваш код проигнорирует CounterException и вывалится с UH.
Еще раз: как можно догадаться, что CounterException user-friendly?
0K>CounterException только для вывода пользователю используется.
я бы переписал код в теле примерно так:
try
{
Console.WriteLine("Введите имя счетчика:");
string alias = Console.ReadLine();
var counter = new Counter(alias); // может бросить ArgumentExceptionvar value = counter.Increment(); // может бросить CounterReadException, CounterIncrementException, CounterWriteException
Console.WriteLine("Текущее значение счетчика: {0}.", value);
}
catch (ArgumentException)
{
Console.WriteLine("Неверно задано имя счетчика");
}
catch (CounterReadException)
{
Console.WriteLine("Ошибка при получении значения счетчика.");
}
catch (CounterIncrementException)
{
Console.WriteLine("Ошибка при увеличении значения счетчика.");
}
catch (CounterWriteException)
{
Console.WriteLine("Ошибка при сохранении нового значения счетчика.");
}
/// Counterpublic class Counter
{
private string _path;
protected Counter() { }
/// <exception cref="ArgumentNullException">alias is null.</exception>
/// <exception cref="ArgumentOutOfRangeException">alias is empty</exception>
/// <exception cref="ArgumentException">Invalid character</exception>public Counter(string alias)
{
if (null == alias)
throw new ArgumentNullException("alias");
if (string.IsNullOrEmpty(alias))
throw new ArgumentOutOfRangeException("alias");
if (alias.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0)
throw new ArgumentException("Invalid character.", "alias");
_path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, alias);
}
/// <exception cref="CounterReadException"></exception>
/// <exception cref="CounterWriteException"></exception>public int Increment()
{
var readValue = Load();
var incrementedValue = InternalIncrement(readValue)
Save(incrementedValue);
}
/// <exception cref="CounterReadException"></exception>protected int Load()
{
// throws CounterReadException on failure
}
/// <exception cref="CounterWriteException"></exception>protected void Save(int value)
{
// throws CounterWriteException on failure
}
/// <exception cref="CounterIncrementException"></exception>protected int InternalIncrement(int readValue)
{
// throws CounterIncrementException on failure
}
}
Очевидно, что за вывод пользователю отвечает Console.WriteLine, за передачу информации о неудаче операции — разные типы исключений. Normal-flow при это прозрачен и не загроможен кучей try/catch блоков.
И да: интеративный режим у консольного приложения подразумевает, что пользователю имена файлов, и прочие "вкусности" будут как "- Ой, Вань, гляди-кось, попугайчики.
Нет, я ей-Богу закричу!"
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, gandjustas, Вы писали:
G>>Это мне вопрос? Я скопипастил.
0K>Вы же переделали. НЕ пытайтесь избежать ответственности за свой код.
я описал функцию, которая увеличивает значение счетчика в файле, использую её для реализации.
using System;
using System.Diagnostics.Contracts;
using System.IO;
using System.Text.RegularExpressions;
public class Program
{
//Изменение счетчикаstatic void Increment(string fileName)
{
Contract.Requires(fileName != null);
Contract.Requires(fileName != string.Empty);
using (var f = File.Open(fileName, FileMode.OpenOrCreate))
{
var reader = new StreamReader(f);
var content = reader.ReadToEnd();
var counter = 0;
if (int.TryParse(content, out counter))
{
counter = (counter == int.MaxValue) ? 0 : (counter + 1);
}
f.Seek(0, SeekOrigin.Begin);
f.SetLength(0);
var writer = new StreamWriter(f);
writer.Write(counter);
}
}
//Любая обработка ошибокstatic bool HandleException(Exception e)
{
Contract.Requires(e != null);
if (e is PathTooLongException
|| e is UnauthorizedAccessException
|| e is DirectoryNotFoundException
|| e is IOException)
{
Console.WriteLine("Операция завершилась неуспешно. Ниже приведено полное описание ошибки.");
Console.WriteLine(e);
return true;
}
return false;
}
//Один цикл пользовательского вводаstatic void UiCycle()
{
Console.WriteLine("Введите имя счетчика:");
string alias = Console.ReadLine();
//Проверку по вкусуif (!Regex.IsMatch(alias, "[a-z]+"))
{
Console.WriteLine("Некорректное имя счетчика (пояснение каким должно быть правильное имя).");
}
Contract.Assume(alias != null);
Contract.Assume(alias != string.Empty);
Increment(alias);
Console.WriteLine("Операция успешно завершена.");
}
static void Main(string[] args)
{
const string YES_SYMBOLS = "YyДд";
while (true)
{
try
{
UiCycle();
}
catch (Exception e)
{
if (!HandleException(e))
throw;
}
Console.WriteLine("Повторить?");
if (!YES_SYMBOLS.Contains(Console.ReadLine()))
break;
Console.Clear();
}
}
}
Получилось в два раза короче чем твой вариант.
Что я хочу показать этим кодом:
1)Если исключение ожидаемо и можно как-то исправить ситуацию, то можно обойтись без исключений (не всегда верно, но по возможности лучше делать так)
2)Unhandled exception policy aka политика обработки неперехваченных исключений должна быть отделена от основной логики, а еще желательно реюзабельна (это отлично достигается с помощью соответствующих средств)
3)Показ сообщения пользователю вполне возможно выполнять без исключений и стоит именно так делать.
4)Операция (или вся программа) должна прерываться при возникновении неперехваченного исключения. Код обработки таких исключений должен быть вне операции.
Здравствуйте, gandjustas, Вы писали:
G>Получилось в два раза короче чем твой вариант.
Ну наконец то вы написали свой вариант. Давайте теперь размебем ваши ошибки (то что он короче -- не показатель, главное качество кода а не его коротковизна).
1. Проверка имени счетчика у вас вынесена в Presentation Layer. Тот, кто использует библиотеку должен знать низкоуровневые детали. Опять-же, если вы измените тип хранилища в своей библиотеке -- прийдется изменять и весь код который библиотеку использует.
2. Контракты вы, видимо, только начинаете использовать. Вы хоть программу то свою запускали?
//Проверку по вкусуif (!Regex.IsMatch(alias, "[a-z]+"))
{
Console.WriteLine("Некорректное имя счетчика (пояснение каким должно быть правильное имя).");
}
Заметили, что если имя счетчика некорректно -- программа продолжит работу с некорректным именем счетчика? Даже никто не исправил пока меня не было.
3. При возникновении ошибки вы не даете возможности пользователю самому понять что произошло. Текст ошибки для пользователя все время одинаковый. Для вас все пользователи -- дураки. А на самом деле дураком окажется тот, кто таковыми считает пользователей -- они будут звонить вам и вы как дурак будете тратить время на их глупые вопросы. Дайте возможность пользователю самому понять что произошло.
G>Что я хочу показать этим кодом: G>1)Если исключение ожидаемо и можно как-то исправить ситуацию, то можно обойтись без исключений (не всегда верно, но по возможности лучше делать так)
В таком случае лучше применить паттерн TryParse, а не то что вы сделали -- вынесли логику в Presentation Layer.
G>3)Показ сообщения пользователю вполне возможно выполнять без исключений и стоит именно так делать.
В простейшем случае (как у меня в примере с именем счетчика -- посмотрите как правильно) -- да. А вот в более сложных случаях -- нужно обязательно вывести конкретизирующую информацию.
Здравствуйте, 0K, Вы писали:
0K>Здравствуйте, gandjustas, Вы писали:
G>>Получилось в два раза короче чем твой вариант.
0K>Ну наконец то вы написали свой вариант. Давайте теперь размебем ваши ошибки (то что он короче -- не показатель, главное качество кода а не его коротковизна).
0K>1. Проверка имени счетчика у вас вынесена в Presentation Layer.
Правильно, потому что PL отвечает за преобразование имени счетчика в имя файла.
0K>Тот, кто использует библиотеку должен знать низкоуровневые детали.
Я про библиотеку писал выше. Да — вызывающий код должен знать что делает функция Increment, и даже то что она работает с файлами. Это знание кстати передается в названии параметра.
0K>Опять-же, если вы измените тип хранилища в своей библиотеке -- прийдется изменять и весь код который библиотеку использует.
Если я изменю тип хранилища — я напишу новую функцию.
0K>2. Контракты вы, видимо, только начинаете использовать.
Ниугадал.
0K>Вы хоть программу то свою запускали?
Не-а
0K>
0K>//Проверку по вкусу
0K>if (!Regex.IsMatch(alias, "[a-z]+"))
0K>{
0K> Console.WriteLine("Некорректное имя счетчика (пояснение каким должно быть правильное имя).");
0K>}
0K>
return забыл.
0K>Заметили, что если имя счетчика некорректно -- программа продолжит работу с некорректным именем счетчика? Даже никто не исправил пока меня не было.
Видимо никто не запускал
0K>3. При возникновении ошибки вы не даете возможности пользователю самому понять что произошло. Текст ошибки для пользователя все время одинаковый.
Нет, он выводится вместе с exception, который содержит локализованный текст ошибки. Также выводится stacktrace, что позволяет программисту определить где была проблема.
Кроме того я сделал пометку, что можно написать любые сообщения об ошибках в коде HandleException, не затрагивая всю остальную логику.
0K>Для вас все пользователи -- дураки. А на самом деле дураком окажется тот, кто таковыми считает пользователей -- они будут звонить вам и вы как дурак будете тратить время на их глупые вопросы. Дайте возможность пользователю самому понять что произошло.
См выше.
Вообще это имеет смысл если пользователь сможет сам исправить ситуацию. А исправить он вероятнее всего сможет в случае UnauthorizedAccessException и DirectoryNotFoundException, и то не факт.
G>>Что я хочу показать этим кодом: G>>1)Если исключение ожидаемо и можно как-то исправить ситуацию, то можно обойтись без исключений (не всегда верно, но по возможности лучше делать так)
0K>В таком случае лучше применить паттерн TryParse
Я применил как раз TryParse, там где исключение ожидаемо и исправимо.
0K>а не то что вы сделали -- вынесли логику в Presentation Layer.
Это ты о чем?
G>>3)Показ сообщения пользователю вполне возможно выполнять без исключений и стоит именно так делать. 0K>В простейшем случае (как у меня в примере с именем счетчика -- посмотрите как правильно) -- да. А вот в более сложных случаях -- нужно обязательно вывести конкретизирующую информацию.
Ну поменяй HandleException и выводи. Ты все еще не понял что:
а)Для вывода конкретизирующей информации в 99,9% случаев не нужны исключения
б)Сам вывод сообщений не касается основной логики работы.
У меня нету цели написать программу, которая работает как ты хочешь. Как минимум потому что я не знаю как ты хочешь чтобы она работала.
Я показываю несостоятельность твоей концепции о различных видах исключений и их применимости, а также показываю как надо писать обработку исключений в большинстве случаев.
...
N> public Result PerformIncrement() {
N> CreateFileIfItDoesNotExist();
N> var oldValue = ReadCurrentValue();
N> var newValue = oldValue + 1;
N> WriteNewValue(newValue);
N> return new Result(oldValue, newValue);
N> }
...
N>
Строчку "var newValue = oldValue + 1;" неплохо бы в блок "checked{}" обрамить, чтобы при переполнении генерировалось исключение.
А то какой-нибудь миллиардер при попытке добавить доллар к своему состоянию в 2147483647 баксов, после выполнения программы расстроится, т.к. его счёт молча уйдёт в глубокие минусА
Здравствуйте, andy1618, Вы писали:
A>Строчку "var newValue = oldValue + 1;" неплохо бы в блок "checked{}" обрамить, чтобы при переполнении генерировалось исключение. A>А то какой-нибудь миллиардер при попытке добавить доллар к своему состоянию в 2147483647 баксов, после выполнения программы расстроится, т.к. его счёт молча уйдёт в глубокие минусА
а у меня в проекте стоит :P
а если по-честному — сравнительно недавно узнал про эту заморочку с непроверяемыми переполнениями. ещё не выработал привычку думать об этом. спасибо за замечание!
а вы сами как — в проекте ставите или каждый раз checked используете?
Здравствуйте, Neco, Вы писали:
N>а у меня в проекте стоит :P
N>а если по-честному — сравнительно недавно узнал про эту заморочку с непроверяемыми переполнениями. ещё не выработал привычку думать об этом. спасибо за замечание!
В этом смысле хорош РEX: выявление переполнений — его конёк! ))
N>а вы сами как — в проекте ставите или каждый раз checked используете?
На проект надежды мало, т.к. код могут при рефакторинге перенести куда-нибудь в другое место.
Обычно делаем так: после написания класса на него пишется юнит-тест, где проверяются граничные значения. Потом смотрим на упавшие тесты — в подавляющем большинстве случаев можно вообще избежать переполнений, применив правильные типы данных. Вот, кстати, поучительный прошлогодний пример: здесь