Отражение или динамическая диспетчеризация

Я пишу абстрактный анализатор файлов (C#), который расширен двумя конкретными анализаторами. Оба должны выполнить несколько проверок. В настоящее время в абстрактном парсере есть метод validate, который использует отражение для вызова всех методов с именем, начинающимся с 'test'. Таким образом, добавление проверок так же просто, как добавление метода с именем, которое начинается с "test".

Недавно у меня появилось несколько комментариев об использовании отражения, и было бы лучше использовать динамическую диспетчеризацию. Мой вопрос к вам: почему бы не использовать рефлексию и как бы вы это реализовали? Также, как я должен использовать динамическую диспетчеризацию для решения этой проблемы?

    public bool Validate()
    {
        bool combinedResult = true;
        Type t = this.GetType();
        MethodInfo[] mInfos = t.GetMethods();

        foreach (MethodInfo m in mInfos)
        {
            if (m.Name.StartsWith("Check") && m.IsPublic)
            {
                combinedResult &= (bool)m.Invoke(this, null);
            }
        }
        return combinedResult;
    }

3 ответа

Решение

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

Мне нравится ваш подход к быстрым и грязным вещам. Что касается кода производственного качества, я вижу следующие потенциальные проблемы с вашим подходом:

  • вы не проверяете типы аргументов и возвращаемые типы, поэтому, если вы или кто-то добавляете метод string CheckWithWrongReturnType тогда ваш код ломается.
  • каждый раз, когда вы вызываете эту функцию, она вызывает GetMethods() и просматривает список. Это, вероятно, неэффективно. Может быть лучше кэшировать этот список в массиве.

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

delegate bool Validator();

bool F1() { return true;  }
bool F2() { return false; }

List<Validator> validators = new List<Validator>(F1, F2);

// тогда в основном классе вы можете сделать это:

foreach(Validator v in validators)
{
   combinedResult &= v();
}

Вы должны использовать обычный ООП, а не отражение для этого. Попросите ли вы, чтобы абстрактный класс раскрыл абстрактный метод, такой как Validate. Каждый парсер должен будет это реализовать. В Validate каждый анализатор вызывает вызывающие методы Check для выполнения работы.

Нет ничего плохого в коде, который работает... пока кто-то не придет его поддерживать. В частности, такого рода соглашение должно быть задокументировано довольно тщательно, поскольку не сразу очевидно, как ваш класс будет делать то, что он делает.

(Тем не менее, использование Reflection для этого будет довольно медленным.)

Наиболее очевидным способом может быть использование абстрактного базового метода. bool Validate() это реализуется подклассами. Подклассы тогда имеют, например,

public override bool Validate()
{
    return TestFirst() && 
        TestSecond() &&  
        TestThird();
 }

Хотя это выглядит громоздко, сразу видно, что происходит. Это также делает модульное тестирование Validate() легкий ветерок.

Также возможно иметь Testxxx() методы регистрируют себя в суперклассе в конструкторе, поэтому они вызываются автоматически, но это более трудоемко и, вероятно, менее обслуживаемо.

Если вы действительно хотите сделать это с отражением, подумайте о Testxxx() методы с атрибутами и размышления о них. Тогда ваш код остается читабельным.

Другие вопросы по тегам