c# - 从架构上讲,我应该如何用更易于管理的东西替换一个非常大的 switch 语句?

标签 c# .net architecture mapping switch-statement

编辑 1: 忘记添加嵌套属性曲线球。

更新:我选择了@mtazva 的回答,因为这是针对我的特定案例的首选解决方案。回想起来,我用一个非常具体的例子问了一个一般性问题,我相信这最终让每个人(或者可能只有我)对这个问题到底是什么感到困惑。我相信一般问题也已得到解答(请参阅策略模式答案和链接)。谢谢大家!

明显的大开关语句smell我已经看到一些关于如何使用 dictionary that maps to functions 执行此操作的链接.但我想知道是否有更好(或更聪明的方法)来做到这一点?在某种程度上,这是一个我一直在脑海中盘旋但从未真正找到好的解决方案的问题。

这个问题源于我之前提出的另一个问题:How to select all the values of an object's property on a list of typed objects in .Net with C#

这是我正在使用的示例类(来自外部来源):

public class NestedGameInfoObject
{
    public string NestedName { get; set; }
    public int NestedIntValue { get; set; }
    public decimal NestedDecimalValue { get; set; }
}

public class GameInfo
{
    public int UserId { get; set; }
    public int MatchesWon { get; set; }
    public long BulletsFired { get; set; }
    public string LastLevelVisited { get; set; }
    public NestedGameInfoObject SuperCoolNestedGameInfo { get; set; }
    // thousands more of these
}

不幸的是,这是来自外部来源...想象一下来自 Grand Theft Auto 或其他东西的巨大数据转储。

我只想获取这些对象列表的一小部分。想象一下,我们希望能够将您与一群 friend 的游戏信息对象进行比较。一个用户的个人结果如下所示:

public class MyResult
{
    public int UserId { get; set; }  // user id from above object
    public string ResultValue { get; set; }  // one of the value fields from above with .ToString() executed on it
}

还有一个我想用更易于管理的东西替换的例子(相信我,我不想维护这个怪物 switch 语句):

const int MATCHES_WON = 1;
const int BULLETS_FIRED = 2;
const int NESTED_INT = 3;

public static List<MyResult> GetMyResult(GameInfo[] gameInfos, int input)
{
  var output = new List<MyResult>();

  switch(input)
  {
    case MATCHES_WON:
        output = gameInfos.Select(x => new MyResult()
         {
            UserId = x.UserId, 
            ResultValue = x.MatchesWon.ToString()
         }).ToList<MyResult>();
      break;

    case BULLETS_FIRED:
        output = gameInfos.Select(x => new MyResult()
         {
            UserId = x.UserId, 
            ResultValue = x.BulletsFired.ToString()
         }).ToList<MyResult>();
      break;

    case NESTED_INT:
        output = gameInfos.Select(x => new MyResult()
         {
            UserId = x.UserId, 
            ResultValue = x.SuperCoolNestedGameInfo.NestedIntValue.ToString()
         }).ToList<MyResult>();
      break;

    // ad nauseum
  }

  return output;
}

所以问题是有什么合理的方法来管理这只野兽吗?我真正想要的是一种动态方式来获取此信息,以防初始对象发生变化(例如,添加了更多游戏信息属性)。有没有更好的方法来构建它以使其不那么笨拙?

最佳答案

我认为您的第一句话没有说明可能是最合理的解决方案:某种形式的字典映射值到方法。

例如,您可以定义一个静态的 Dictionary<int, func<GameInfo, string>> ,其中每个值(例如 MATCHES_WON)都将添加相应的 lambda 以提取适当的值(假设您的常量等已按照您的示例中所示定义):

private static Dictionary<int, Func<GameInfo, string>> valueExtractors =
    new Dictionary<int, Func<GameInfo, string>>() {
        {MATCHES_WON,   gi => gi.MatchesWon.ToString()},
        {BULLETS_FIRED, gi => gi.BulletsFired.ToString()},
        //.... etc for all value extractions
    };

然后您可以使用这个字典来提取示例方法中的值:

public static List<MyResult> GetMyResult(GameInfo[] gameInfos, int input)
{
  return gameInfo.Select(gi => new MyResult()
         {
            UserId = gi.UserId, 
            ResultValue = valueExtractors[input](gi)
         }).ToList<MyResult>();
}

除此选项外,您可能会使用编号和属性名称进行某种文件/数据库/存储查找,然后使用反射来提取值,但显然效果不佳。

关于c# - 从架构上讲,我应该如何用更易于管理的东西替换一个非常大的 switch 语句?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7396781/

相关文章:

c# - 从当前文化中获取货币?

C#线程间通信

c# - 运行时异常捕获在 MSVS 内部和外部之间不同

c# - 验证 .NET Windows 窗体的正确方法是什么?

c# - Entity Framework - 一对一 - ReferentialConstraint 映射到存储生成的列

architecture - ZeroMQ 服务和数据访问

architecture - 什么时候应该使用Logic App而不是Web Job?

optimization - 为什么编译器不生成微指令而不是汇编代码?

c# - FetchXML 过滤掉重复值

c# - 在匿名方法中命名参数