Как проверить данные в полях интерфейса C #? - PullRequest
2 голосов
/ 23 февраля 2011

У меня есть несколько классов, которые реализуют определенный интерфейс.

Есть ли способ на уровне интерфейса, а не на уровне реализации, определить правила проверки данных?

Если нет, то каков будет предложенный шаблон, чтобы отделить правила проверки данных от определенных классов? (РЕДАКТИРОВАТЬ: В моем случае я хотел бы избежать использования абстрактного базового класса для реализации проверки.)

Спасибо

Ответы [ 7 ]

8 голосов
/ 23 февраля 2011

Я бы разделил логику проверки на другой класс. Например, если ваш интерфейс IFoo, у вас будет FooValidator с методом Validate(IFoo foo). Это отделяет реализацию IFoo от бизнес-правил, связанных с проверкой. Разделение означает, что:

  • Вы используете один и тот же класс валидатора для всех реализаций IFoo
  • Логика проверки не зависит от конкретной реализации IFoo
  • Вы можете использовать разные валидаторы в разных контекстах, поэтому у администратора может быть меньше правил валидации, чем у клиента, или вы можете смоделировать валидацию для автоматических тестов

В этом примере реализации используется абстрактный класс ValidatorBase, который вам не нужно использовать изначально, я его преждевременно оптимизировал: - $.

interface IFoo
{
    int Age { get; }
    string Name { get; }
}
class Foo : IFoo
{
    public int Age { get; set; }
    public string Name { get; set; }
}

abstract class ValidatorBase<T>
{
    public class Rule    
    {
        public Func<T, bool> Test { get; set; }
        public string Message { get; set; }
    }

    protected abstract IEnumerable<Rule> Rules { get; }

    public IEnumerable<string> Validate(T t)
    {
        return this.Rules.Where(r => !r.Test(t)).Select(r => r.Message);
    }
}

class FooValidator : ValidatorBase<IFoo>
{
    protected override IEnumerable<ValidatorBase<IFoo>.Rule> Rules
    {
        get
        {
            return new Rule[] {
                new Rule { Test = new Func<IFoo,bool>(foo => foo.Age >= 0), Message = "Age must be greater than zero" },
                new Rule { Test = new Func<IFoo,bool>(foo => !string.IsNullOrEmpty(foo.Name)), Message = "Name must be provided" }
            };                    
        }
    }
}

static void Main(string[] args)
{
    var foos = new[] {
        new Foo { Name = "Ben", Age = 30 },
        new Foo { Age = -1 },
        new Foo { Name = "Dorian Grey", Age = -140 }
    };

    var fooValidator = new FooValidator();

    foreach (var foo in foos)
    {
        var messages = fooValidator.Validate(foo);
        if (!messages.Any()) Console.WriteLine("Valid");
        else foreach (var message in messages) Console.WriteLine("Invalid: " + message);
        Console.WriteLine();
    }
}

Запуск программы дает такой результат:

Действительно

Invalid: возраст должен быть больше нуля
Неверно: необходимо указать имя

Invalid: возраст должен быть больше нуля

5 голосов
/ 23 февраля 2011

Может быть, вы используете абстрактный класс как "человек посередине", где вы можете размещать правила проверки свойств?

2 голосов
/ 23 февраля 2011

Другая возможность - использовать атрибуты.Вы можете создать собственный атрибут для определения правил проверки

public abstract class ValidationAttribute : Attribute
{
    public abstract bool IsValid(object value);
}

public class EvenValidation : ValidationAttribute
{
     public override bool IsValid(object value)
     {  
          if (!(value is int))
             return false;

          return ((int)value) % 2 == 0;
     }
}

public interface IFoo
{
     [EvenValidation]
     int SomeValue { get; }
}

public static class Validator
{
      public static bool IsValid(object component, object proposedValue, string property) 
      {
           //Use reflection to look for ValidationAttributes on the property
           //Use the ValidationAttribute to validate the proposed value
      }
}
2 голосов
/ 23 февраля 2011

Возможно ли, чтобы все эти классы были производными от общего базового класса и реализовали там вашу логику проверки? Вы можете иметь общий базовый класс, реализующий интерфейс, и определять абстрактные методы, которые производные классы будут реализовывать для настройки поведения.

0 голосов
/ 23 февраля 2011

Вы не упомянули, используете ли вы WinForms, WPF и т. Д., И, как говорят другие люди, вы не можете поместить этот код в реальные интерфейсы.

Предлагаемый способ - реализовать IDataErrorInfo набазовый класс.

Вот пример того, как кто-то реализует это в базовом классе при использовании шаблона MVVM: IDataErrorInfo с MVVM

Вам не обязательно следовать MVVM,но вы можете следить за его статьей, в которой реализована проверка для WPF.

Тем не менее, это немного неуклюже, поскольку вам приходится работать с каждым из ваших свойств.Который вы можете обойти, только поместив некоторые из этих свойств в общий базовый класс.Есть более сложные способы обойти это, если вы хотите попробовать что-то вроде Castle Windsor и настроить AOP , но я бы не советовал включать это в частично заполненное приложение.

0 голосов
/ 23 февраля 2011

Во-первых, вы не можете иметь никакой реализации в самом интерфейсе.

Если у вас есть такое требование, вам лучше заниматься абстрактным классом.

0 голосов
/ 23 февраля 2011

Вы не можете реализовать какой-либо код на уровне интерфейса - поэтому вы не можете поместить проверочный логин в интерфейс.

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

Для совместного использования кода проверки просто используйте отдельный метод, который принимает интерфейс в качестве параметра, и выполняйте проверку там, например bool Validate(IMyInterface data){... return true;}.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...