Pull to refresh

О том, как можно проверять значения, введёные пользователем

Reading time3 min
Views13K
В любых программных продуктах, будь то windows-приложение или web-сайт, получение информации от пользователей зачастую осуществляется с помощью форм ввода данных.

Конечно же, нельзя быть абсолютно уверенным, что пользователь введёт именно то, что нужно, поэтому все данные необходимо тщательно проверить.

Как правило, алгоритм проверки этих данных один и тот же: «Если значение поля удовлетворяет требованию, то проверить следующее требование, иначе вывести сообщение об ошибке. Перейти к проверке значения следующего поля».

На практике это выливается с довольно длинные последовательности «if-else». Лично мне это жутко не нравилось, так как сложно с первого взгляда определить, какие поля как проверяются и какие сообщения выдаются в случае ошибок. А ведь полей в форме может быть и десять, тогда код проверки вообще затягивается. Вобщем, я задумался над тем, как можно минимизировать объём работ и вот что из этого получилось.

Я представил проверку как преобразование значения одного типа в значение другого типа (например, проверка того, что в поле введено чило, это преобразование строки в число). Тоесть проверка — это некая функция, имеющая сигнатуру делегата System.Converter<T, U>.

Для проверки значение помещаем в класс обёртку:

public class ValidationStep<T>
{
  public T Value { get; set; }
}


Суть проверки заключается в последовательном вызове методов-расширений для объектов класса ValidationStep<T>, которые опять же возращают объект класса ValidationStep<T>. Это позволяет создавать цепочки проверок. Вот пример таких методов-расширений:

public static class ValidationExtensions
{
  public static ValidationStep<T> Validate<T>(this T value)
  {
    return new ValidationStep<T> { Value = value };
  }
  public static ValidationStep<T> Validate<T>(this ValidationStep<T> step,
    Predicate<T> predicate, string message)
  {
    if (predicate(step.Value)) return step;
    throw new ValidationException(message);
  }
  public static ValidationStep<U> Convert<T, U>(this ValidationStep<T> step,
    Converter<T, U> converter, string message)
  {
    try { return converter(step.Value).Validate(); }
    catch { throw new ValidationException(message); }
  }
}


Первый метод используется, чтобы можно было проверять объекты любых типов, торой метод осуществляет проверку на соответствие предикату, а третий на возможность преобразования значения из одного типа в другой. Также можно написать любые другие проверки, например на соответствие целого числа диапазону. Главное, что в случае неудачной проверки должно возникать исключение ValidationException, которое содержит сообщение с текстом ошибки.

Для осуществления самой проверки можно использовать следующий класс, который будет заниматься перехватом исключений:

public static class Validation
{
  public static bool Validate<T, U>(T value, Converter<T, U> validator,
    Action<U> onSuccess, Action<ValidationException> onFailure)
  {
    try
    {
      var result = validator(value);
      onSuccess(result);
      return true;
    }
    catch (ValidationException e)
    {
      onFailure(e);
      return false;
    }
  }
}


А теперь о том, как это использовать. Допустим нам нужно проверить текстовое поле (tb1) и убедиться, что в него введено целое число в диапазоне от 0 до 10. Это можно сделать так:

Validation.Validate(
  tb1.Text, value => value.Validate()
    .Validate(x => x.Length > 0, "Введите что-нибудь")
    .Convert(x => Convert.ToInt32(x), "Введёное значение не является целым числом")
    .Validate(x => x >= 0, "Число не должно быть меньше нуля")
    .Validate(x => x <= 10, "Число не должно быть больше десяти"),
  v => MessageBox.Show("Введено корректное значение: " + v.Value),
  e => MessageBox.Show(e.Message));


Учитывая, что проверок может быть больше, да и число полей в форме, как правило, больше одного, такой способ проверок может быть очень даже удобен.

Ну вот, собственно, и всё: о)
Tags:
Hubs:
Total votes 35: ↑29 and ↓6+23
Comments61

Articles