В принципе, вам необходимо нанести на карту CommandNExcecutor
тип до CommandN
тип.
1) Использовать словарь. Это самый простой способ:
private static readonly Dictionary<Type, Type> map = new Dictionary<Type, Type>
{
{ typeof(Command1), typeof(Command1Executor) },
{ typeof(Command2), typeof(Command2Executor) },
...
};
List<CommandResult> Execute(List<CommandBase> commands)
{
return commands
.Select(command =>
{
var executor = Activator.CreateInstance(map[command.GetType], command);
return executor.Execute();
})
.ToList();
}
2) Использовать метаданные (атрибуты). Это соответствует сценариям на основе плагинов, когда типы команд могут добавляться динамически, без восстановления основного функционала. Это может быть ваша собственная реализация или существующая реализация DI-контейнера (многие из них предоставляют API-интерфейсы метаданных).
[AttributeUsage(AttributeTargets.Class)]
public sealed class CommandExecutorAttribute : Attribute
{
public CommandExecutorAttribute(Type commandType)
{
CommandType = commandType;
}
public Type CommandType { get; }
// ...
}
[CommandExecutor(typeof(Command1))]
public sealed class Command1Executor : ICommandExecutor
{
// ...
}
List<CommandResult> Execute(List<CommandBase> commands)
{
return commands
.Select(command =>
{
// obtain executor types somehow, e.g. using DI-container or
// using reflection;
// inspect custom attribute, which matches command type
var executorType = ....
var executor = Activator.CreateInstance(executorType , command);
return executor.Execute();
})
.ToList();
}
UPDATE.
Если вы хотите, чтобы избежать отражений, в первом случае просто заменить параметр типа для значения в словаре, чтобы Func<CommandBase, ICommandExecutor>
:
private static readonly Dictionary<Type, Func<ICommandExecutor>> map = new Dictionary<Type, Func<ICommandExecutor>>
{
{ typeof(Command1), command => new Command1Executor(command) },
{ typeof(Command2), command => new Command2Executor(command) },
...
};
Это позволит создать исполнитель через делегат вместо отражения:
var executor = map[command.GetType](command);
Второй случай не может избежать отражение полностью, так как вам нужно как-то получить типы исполнителей. Но это может привести к случаю 1 (со словарем).
Сделать ленивых map
:
private static readonly Lazy<Dictionary<Type, ConstructorInfo>> map = ...
Затем на Lazy<T>
инициализации, делать все отражения работы. Так как это static Lazy<T>
, вы будете делать это один раз в домене приложения. Вызов ConstructorInfo
достаточно быстр. Следовательно, вы получаете производительность только один раз, когда обрабатываете первую команду.
Других варианты (все они предполагают Lazy<Dictionary>
) для случая 2 является:
- вместо отражения
ConstructorInfo
, строить Expression<Func<CommandBase, ICommandExecutor>>
с с использованием ConstructorInfo
, обобщать их и поместить делегат в словарь - это будет таким же, как делегаты case 1, но с динамически поддерживаемыми командами;
- использование DI-контейнера, который испускает IL для построения зависимостей (AFAIK, NInject делает это);
- испустить ИЛ самостоятельно (ИМО, это полностью заново изобрести колесо).
И, наконец, решить эту проблему самым простым способом, затем производительность мера, затем рассмотреть более сложный путь. Избегайте преждевременной оптимизации. Я ничего не знаю о том, что вы командуете природой, но я подозреваю, что выполнение этой команды много дольше, чем что-то отражающее (конечно, есть шанс, что я ошибаюсь).
Надеюсь, это поможет.
Поместите все команды исполнителей (или functons, которые создают их) в словаре, в привязке по типу команд. Таким образом вам не понадобится, если еще целая цепочка. – Evk