#c# #.net
#c# #.net
Вопрос:
Я ищу способ улучшить код следующей структуры:
foreach (var operation in operations)
{
if (IsTypeAndDoSomething<Type1>(operation))
{
Run((Type1)operation);
}
else if (IsTypeAndDoSomething<Type2>(operation))
{
Run((Type2)operation);
}
etc.
Я бы предпочел избежать длинного списка else if
блоков с практически одинаковым шаблоном повсюду.
В этом проекте мы создаем перегруженный метод Run для выполнения каждой операции. Когда выполняется список операций, нам нужно сначала определить тип каждой операции, а затем подготовить ее (часть doSomething), прежде чем запускать ее Run
. В настоящее время повторяется еще один подход, если кажется необходимым.
Однако мне интересно, есть ли в C # какой-либо способ сделать приведенный выше код более «общим» без повторяющихся else if
s.
Одна из идей заключалась в создании коллекции типов, например
Мне было интересно, есть ли какой-либо способ преобразовать список типов в параметры универсального типа?
var types = new[] {typeof(Type1), typeof(Type1)};
Затем, когда мы добавим новую реализацию операции, мы можем просто добавить ее в эту коллекцию (вместо нескольких else if
блоков у нас будет другой итератор).
Однако нам нужно было бы каким-то образом передавать каждый элемент как Tx:
IsTypeAndDoSomething<Tx>
Кажется невозможным ссылаться на эти типы в параметре универсального типа (T). Я предполагаю, что это связано с тем, что это делается во время компиляции, а не во время выполнения.
Возможно ли это или рекомендуется другой шаблон?
Комментарии:
2. Есть ли у вас контроль над
Type1
и т.Type2
Д.? Можете ли вы добавить к ним методы и заставить их реализовать общий интерфейс? Если это так, вам следует использовать полиморфизм.3. Почему бы не использовать полиморфизм? рефакторинг.guru/replace-conditional-with-polymorphism может быть связан с динамическим программированием или отражением.
4. Что
IsTypeAndDoSomething
именно делает?5. Чтобы уточнить комментарий Cid — сопоставление шаблонов с использованием switch
Ответ №1:
Альтернативой сопоставлению шаблонов может быть использование шаблона visitor:
Вы вводите пользовательский интерфейс
interface IOperationsVisitor<T> {
T Visit(Type1 obj);
T Visit(Type2 obj);
}
И добавьте метод Accept в базовый класс для ваших операций:
T Accept(IOperationsVisitor<T> visitor) => visitor.Visit(this);
Затем вы просто реализуете интерфейс посетителя и вызываете Accept
метод для своего объекта с объектом посетителя в качестве параметра.
Это намного более громоздко, чем сопоставление с образцом, но преимущество заключается в том, что если вы введете новый тип, вам придется обновлять всех посетителей для обработки нового типа. При сопоставлении с шаблоном может быть легко забыть все места, где проверяется тип.
Ответ №2:
Простое изложение вопроса помогло мне понять, что ответ заключается в преобразовании в неродовой метод, т.е.
private bool IsTypeAndDoSomething(Type type, object operation)
С этим изменением тип обрабатывается во время выполнения, и поэтому можно заменить повторяющиеся совпадающие блоки else if на следующие:
var types = new[] {
typeof(Type1),
typeof(Type2)
};
foreach (var operation in operations)
{
foreach (var type in types)
{
if (IsTypeAndDoSomething(type, operation))
{
Run(operations, (dynamic) operation);
}
}
}
Комментарии:
1. Это кажется хуже, чем оригинал. Общий всегда предпочтительнее системы. Введите объект, когда у вас есть значение
2. Первоначальное требование здесь состоит в том, чтобы избежать длинного блока else if с повторяющимися другими ifs (каждый из которых отличается только по типу). Я не могу придумать общий способ сделать это (поскольку тип известен только во время выполнения), но я приветствую любые мысли. Я обновил свой пример кода выше, чтобы показать более полное решение.