【问题标题】:Replace switch statements for child class type替换子类类型的 switch 语句
【发布时间】:2019-04-12 19:40:41
【问题描述】:

有一个方法接受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 语句的情况下确定子类和函数?

第二个:在 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]);

【问题讨论】:

  • 创建一个所有类都实现的通用基类/接口,然后简单地调用myInstance.TheSingleFunction()?
  • 如您所见,已经有一个带有虚函数的基类,我确实从像p.function3() 这样的父类调用函数。此外,在该项目中创建界面不会按设计进行。
  • 但是你的基类中没有三个方法,只需要一个所有 yopur 派生类都实现的方法。这样您就不必打开方法,而只需打开类型。
  • 可能字典风格更好——在使用中然后切换,切换在少数情况下很好,否则可读性很差。性能相似 - 开关变成了基于哈希的搜索
  • 如果您传入类型而不是 int 并使用 Activator.CreateInstance();,则可以轻松解决第一个开关

标签: c# .net switch-statement mapping


【解决方案1】:

我对你原来的代码做了一些修改,我把输入参数改成了字符串,假设你可以直接把名字作为输入。然后使用第一种方法实例化并使用第二种方法调用。如果你想继续使用 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();
}

也总是推荐切换到更好的设计。

【讨论】:

    【解决方案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
    };
    

    那么你的方法仍然可以取两个整数并返回一个字符串:

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

    请注意,代码是直接写在这里的,没有经过测试,所以我可能遗漏了一两件事,但这是大体思路。

    【讨论】:

    • 很高兴为您提供帮助 :-)
    猜你喜欢
    • 2022-08-17
    • 2012-05-26
    • 2016-08-13
    • 2014-11-01
    • 2021-11-10
    • 1970-01-01
    相关资源
    最近更新 更多