替换子类类型的开关语句

时间:2018-11-09 08:37:41

标签: c# .net switch-statement mapping

有一种方法可以接受2个参数:

int selectedClass;
int selectedFunction;

接下来,有2条switch语句。首先,它使用枚举确定子类类型:

ParentClass p;
switch(selectedClass){
    case (int)ClassTypes.A:
        p = new classA();
        break;
    case (int)ClassTypes.B:
        p = new classB();
        break;
    case (int)ClassTypes.C:
        p = new classC();
        break;
}

然后继续执行50多个语句。此外,还有另一个switch语句确定功能:

string result;
switch(selectedFunction){
    case (int)FunctionTypes.Func1:
        result = p.function1();
        break;
    case (int)FunctionTypes.Func2:
        result = p.function2();
        break;
    case (int)FunctionTypes.Func3:
        result = p.function3();
        break;
}

我确实使用了搜索,有很多改进第二个switch语句的示例,但没有一个改进第一个switch语句的示例。第一个问题是:如何在没有switch语句的情况下确定子类和函数?

2nd:在js中,我会做类似的事情:

functionsArray[selectedClass][selectedFunction]();

是否可以在c#中实现类似的映射?

更新#1: 我用以下代码替换了第一开关:

public static Dictionary<ClassTypes, Type> typeDict = new Dictionary<ClassTypes, Type>()
{
    { ClassTypes.A   , typeof(classA) },
    { ClassTypes.B   , typeof(classB) },
    { ClassTypes.C   , typeof(classC) }
};

ParentClass p = (ParentClass)Activator.CreateInstance(typeDict[selectedClass]);

2 个答案:

答案 0 :(得分:2)

我不能说我理解导致您选择这个奇怪设计的逻辑,但是我可以想到至少两种改进它的方法,只要您要调用的所有功能都在基类中实现(并重写)即可。当然,在派生类中需要的时候。

这两个解决方案仅在所有类都提供了无参数的构造函数和无参数的函数且执行这些函数不需要进一步初始化的情况下才相关:

第一个解决方案将要求您更改方法签名,并强制调用方法知道类的类型,因此您可能无法实现它,但是涉及的代码要少得多。

ExecuteMethod<TClass>(Func<TClass, string> func) where T: BaseClass, new()
(
    return func(new T());
)

您这样称呼它:

var result = ExecuteMethod<ClassA>(a => a.Function1);

第二个解决方案
这可能更适合您的需求: 您将需要创建两个字典并填充它们,如下所示:

private Dictionary<int, Func<ParentClass>> constructors = new Dictionary<int, Func<ParentClass>>()
{
    {1, () => new ClassA()},
    {2, () => new ClassB()}
    // more of the same
};

private Dictionary<int, Func<ParentClass, string>> methods = new Dictionary<int, Func<ParentClass, string>>()
{
    {1, i => i.function1},
    {2, i => i.function2}
    // more of the same
};

然后您的方法仍然可以采用两个int并返回一个字符串:

string DoSomething(int classType, int function)
{
    var instance = constructors[classType].Invoke();
    return methods[function].Invoke(instance);
}

请注意,代码是直接在此处编写的,未经测试,因此我可能错过了一两件事,但这是总体思路。

答案 1 :(得分:0)

我对您的原始代码进行了一些更改,假设您可以直接将名称作为输入,我将输入参数更改为字符串。然后使用第一种方法进行实例化,然后使用第二种方法进行调用。如果您想继续使用Enum,我已经添加了一个重载。

string selectedClass;
string selectedFunction;

public object GetClassInstanceFromName(string name) 
{
    object type =  Type.GetType($"{this.GetType().Namespace}.{name}";
    return Activator.CreateInstance((Type)type);  
}

public string InVokefunctionByName(object instance,string methName)
{
    return instance.GetType().GetMethod(methName).Invoke(instance, null) as string;     
}

//Overload if you want to continue to use your enum
public object GetClassInstanceFromName(ClassTypes name)
{
    return 
     Activator.CreateInstance(Assembly.GetExecutingAssembly().FullName,
     "class" +name.ToString());
}

private void Test()
{
   object a = GetClassInstanceFromName(selectedClass);
   Console.WriteLine(InVokefunctionByName(a, selectedFunction));
   Console.ReadKey();
}

始终建议您切换到更好的设计。