Здравствуйте, 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 используете?
На проект надежды мало, т.к. код могут при рефакторинге перенести куда-нибудь в другое место.
Обычно делаем так: после написания класса на него пишется юнит-тест, где проверяются граничные значения. Потом смотрим на упавшие тесты — в подавляющем большинстве случаев можно вообще избежать переполнений, применив правильные типы данных. Вот, кстати, поучительный прошлогодний пример: здесь