Отражение или динамическая диспетчеризация - PullRequest
3 голосов
/ 30 ноября 2009

Я пишу абстрактный анализатор файлов (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 ]

3 голосов
/ 30 ноября 2009

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

2 голосов
/ 30 ноября 2009

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

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

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

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

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

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

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

0 голосов
/ 30 ноября 2009

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

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

  • вы не проверяете типы аргументов и возвращаемые типы, поэтому, если вы или кто-то добавляете метод 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();
}
...