Как можно уйти от монструозных switch case?
В общем, есть метод:
public BaseType CreateType(SomeEnum enum,SomeParam param,SomeParam2 param2)
{
switch (enum)
{
case SomeEnum.enum1:
case SomeEnum.enum2:
case SomeEnum.enum3:
return new SomeType1(param.Param1,param.Param2)
case SomeEnum.enum4:
var val=param.Param1+param.Param2;
return new SomeType2(val,param2.Param1)
...
(Немного обфусцировал его, но суть должна быть понятна)
Проблема в большом switch и добавляя новые значения в перечисления он становится все больше и больше...
Как от этого можно элегантно уйти? Видел в интернете решения через словарь <enum,action>, но кардинально ничего не поменяется.
Ответы (2 шт):
Для начала надо что-то сделать с параметрами - у вас их слишком много, такое количество параметров по цепочке передавать неудобно:
class CreateTypeContext
{
public SomeParam Param { get; set; }
public SomeParam2 Param2 { get; set; }
// также этот класс можно сделать иммутабельным или вообще record type из C# 9.0 - к обсуждаемому вопросу это отношения не имеет
}
Дальше в простейшем случае этот контекст можно передавать прямо в конструкторы конкретных типов. Ну или же можно сделать по отдельному методу на конкретный тип:
BaseType CreateType1 (CreateTypeContext context)
{
var val = context.Param1.Foo + context.Param1.Bar;
return new SomeType2(val, context.Param2.Baz);
}
Дальше можно сделать пользовательской атрибут и навесить его на метод - это позволит уйти от конструкции switch (если вы передаёте контекст в конструктор - атрибут лучше навешивать на класс):
[AttributeUsage(AttributeTargets.Method)]
sealed class SomeEnumAttribute : Attribute
{
public IReadOnlyCollection<SomeEnum> Kinds { get; }
public SomeEnumAttribute(params SomeEnum[] kinds)
{
Kinds = kinds;
}
}
// …
[SomeEnum(SomeEnum.enum4)]
BaseType CreateType1 (CreateTypeContext context) { … }
Теперь осталось только получить список всех методов через рефлексию, найти среди них метод с нужным атрибутом и вызвать его. Только если вы так будете делать многократно - лучше соберите заранее словарь делегатов:
class BaseTypeFactory
{
private readonly Dictionary<SomeEnum, Func<CreateTypeContext, BaseType>> factories;
public BaseTypeFactory()
{
factories = (
from method in GetType().GetMethods(BindingFlags.Instance | BindingFlags.NonPublic)
let attr = method.GetCustomAttribute<SomeEnumAttribute>()
where attr != null
let fn = (Func<CreateTypeContext, BaseType>)Delegate.CreateDelegate(typeof(Func<CreateTypeContext, BaseType>)), this, method)
from kind in attr.Kinds
select (kind, fn)
).ToDictionary(x => x.kind, x => x.fn);
}
public BaseType CreateType(SomeEnum kind, SomeParam1 param1, SomeParam2 param2)
{
var context = new CreateTypeContext
{
Param1 = param1,
Param2 = param2,
};
if (factories.TryGetValue(kind, out var factory))
return factory(context);
// …
}
// методы создания
}
Не уверен, что это будет подходить конкретно для Вашего случая, но есть вариант использовать паттерн Strategy.
using System;
public class Program
{
public static void Main()
{
TypeCreator typeCreator = new TypeCreator();
typeCreator.SetStrategy(new Strategy1());
var a = typeCreator.CreateType(new int[] {1});
Console.WriteLine(a);
typeCreator.SetStrategy(new Strategy2());
var b = typeCreator.CreateType(new int[] {1, 2});
Console.WriteLine(b);
}
abstract class BaseType{}
class SomeType1 : BaseType{
private int _a;
public SomeType1(int a){
this._a = a;
}
public override string ToString() { return _a.ToString();}
}
class SomeType2 : BaseType{
private int _a;
private int _b;
public SomeType2(int a, int b){
this._a = a;
this._b = b;
}
public override string ToString() { return _a.ToString() + " " + _b.ToString();}
}
class TypeCreator {
private IStrategy _strategy;
public void SetStrategy(IStrategy strategy){
this._strategy = strategy;
}
public BaseType CreateType(int[] parameters) {
return _strategy.Execute(parameters);
}
}
interface IStrategy {
BaseType Execute(int[] parameters);
}
class Strategy1 : IStrategy{
public BaseType Execute(int[] parameters){
return new SomeType1(parameters[0]);
}
}
class Strategy2 : IStrategy{
public BaseType Execute(int[] parameters){
return new SomeType2(parameters[0], parameters[1]);
}
}
}