从架构上讲,我应该如何用更易于管理的东西替换极大的switch语句?

lon*_*gda 6 .net c# architecture mapping switch-statement

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

更新:我选择了@ mtazva的答案,因为这是我特定案例的首选解决方案.回想起来,我用一个非常具体的例子问了一个普遍的问题,我认为这最终会使每个人(或者可能只是我)混淆这个问题究竟是什么.我相信一般问题也得到了回答(参见战略模式答案和链接).感谢大家!

很大的开关语句显然闻到了味道,我已经看到了一些关于如何使用映射到函数字典来实现这一点的链接.但我想知道是否有更好(或更聪明的方法)来做到这一点?在某种程度上,这是一个我总是在脑后滚动的问题,但从来没有真正有一个很好的解决方案.

这个问题源于我之前提到的另一个问题:如何使用C#在.Net中的类型对象列表中选择对象属性的所有值

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

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
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,这来自外部资源......想象一下来自侠盗猎车手的巨大数据转储等等.

我想得到这些对象列表的一小部分.想象一下,我们希望能够将您与一群朋友的游戏信息对象进行比较.一个用户的单个结果如下所示:

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
}
Run Code Online (Sandbox Code Playgroud)

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

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;
}
Run Code Online (Sandbox Code Playgroud)

所以问题是有没有合理的方法来管理这个野兽?我真正喜欢的是在初始对象发生变化(例如,添加更多游戏信息属性)的情况下获取此信息的动态方式.有没有更好的方法来设计这个,所以它不那么笨拙?

mta*_*zva 8

我认为你的第一句话可能是最合理的解决方案:某种形式的字典映射值到方法.

例如,您可以定义一个静态Dictionary<int, func<GameInfo, string>>,其中每个值(如MATCHES_WON)都将添加一个相应的lambda,该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
    };
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用此字典提取示例方法中的值:

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

在此选项之外,您可能会使用数字和属性名称进行某种文件/数据库/存储查找,然后使用反射来提取值,但这显然不会执行.


Eni*_*ity 5

我认为这段代码已经失控了.您正在有效地使用常量来索引属性 - 这就是创建脆弱的代码,您希望使用某些技术 - 例如 - 反射,字典等 - 来控制增加的复杂性.

实际上,您现在使用的方法最终会得到如下代码:

var results = GetMyResult(gameInfos, BULLETS_FIRED);
Run Code Online (Sandbox Code Playgroud)

另一种方法是定义一个允许您执行此操作的扩展方法:

var results = gameInfos.ToMyResults(gi => gi.BulletsFired);
Run Code Online (Sandbox Code Playgroud)

这是强类型的,它不需要常量,switch语句,反射或任何神秘的东西.

只需编写这些扩展方法,您就完成了:

public static class GameInfoEx
{
    public static IEnumerable<MyResult> ToMyResults(
        this IEnumerable<GameInfo> gameInfos,
        Func<GameInfo, object> selector)
    {
        return gameInfos.Select(gi => gi.ToMyResult(selector));
    }

    public static MyResult ToMyResult(
        this GameInfo gameInfo,
        Func<GameInfo, object> selector)
    {
        return new MyResult()
        {
            UserId = gameInfo.UserId,
            ResultValue = selector(gameInfo).ToString()
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

那对你有用吗?

  • @longa - 我认为你不需要案例陈述或同等学历.听起来你正在以艰难的方式做事.可能值得尝试更详细地解释您正在尝试做的事情.`COMPARE_BULLETS_FIRED`评论根本没有告诉我. (2认同)