Здравствуйте, 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" потому как ЛЮБОЙ факт ошибки чтения конфига в такой ситуации свидетельствует о создании нештатной ситуации и неизвестно — чего там еще не так.