3

我处于循环数据并根据设置以特定方式对其进行格式化的场景中,我担心我认为最好的风格可能会阻碍性能。

代码基本模式如下

enum setting {single, multiple, foo, bar};
Data data = getData(Connection conn, int id);
setting blah = data.getSetting();
foreach (Item item in data)
{
   switch(blah)
   {
      case blah.single:
        processDataSingle(item blah);
        break;
      ...
   }
}

我担心的是数据中可能有数千甚至数万个项目。我想知道是否将开关置于循环中可能会重复评估它可能会导致一些严重的性能问题。我知道我可以将循环放在switch循环之前,但是每个循环都case包含它,这似乎不太可读,因为基本功能保持不变的情况不太明显。

4

6 回答 6

10

您可以设置一次委托/操作,然后每次在循环中调用它:

Data data = getData(Connection conn, int id);
setting blah = data.getSetting();
Action<Item> doThis;
switch (blah)
{
  case blah.single:
      doThis = i => processSingleData(i blah);
      break;
  ...
}
foreach (Item item in data)
{
    doThis(item);
}

基本上,将每个“案例”的主体放在 a 中,在循环外Action选择它,然后在循环中调用。ActionswitchAction

于 2013-02-28T15:36:08.377 回答
2

您可以创建一个方法来保持可读性,然后将数据传递给该方法:

void processAllData(IEnumerable<Item> data,  setting blah)
{
    switch(blah)
    {
      case blah.single:
        foreach (Item item in data)
        {

        }
    }
    // next case, next loop ...
}

然后它只是一个单行:

processAllData(data, blah);

这种方法是可读的,因为它封装了复杂性,简洁,因为你只看到你必须看到的,高效,因为你可以优化案例。

于 2013-02-28T15:38:57.620 回答
1

通过这种方式使用 Action 委托,您可以大量分解代码

enum setting {single, multiple, foo, bar};
Data data = getData(Connection conn, int id);

var processAll = new Action<Action<item>>(action =>
                    {
                        foreach(var item in data)                           
                            action(item);
                    });

setting blah = data.getSetting();

switch(blah)
{
    case blah.single:
       processAll(item => processDataSingle(item, blah));
       break;
       ...
}
于 2013-02-28T15:42:38.463 回答
1

如果您谈论可能运行数万次或更多次的比较,它肯定有可能影响性能。您在此处编写的代码中可能出现的另一个问题是,如果您需要添加到枚举中会发生什么。然后,您需要打开此代码并对其进行调整以处理这种情况,这违反了Open/Closed Principle

IMO 同时解决这两个问题的最好方法是使用工厂模式来解决这个问题(请参阅此处此处的帖子以获取有关启动该问题的一些建议)。您需要做的就是拥有一个接口,其实现将调用您想要在上面的 switch 代码中调用的方法。创建一个工厂并让它根据传入的枚举选择哪个实现返回到您的代码(在循环之前)。此时,您的循环需要做的就是调用该接口方法,该方法将完全按照您的要求执行。

之后,任何未来的功能添加只需要您创建该接口的另一个实现,并相应地调整枚举。没有混乱,没有大惊小怪。

于 2013-02-28T15:42:44.470 回答
0

像这样将开关置于循环中几乎肯定会更慢。是否重要无法说 - 使用秒表查看。

于 2013-02-28T15:37:49.933 回答
0

如果 switch 语句中的值彼此接近,编译器将生成一个查找表而不是 N 个if语句。它提高了性能,但很难说编译器何时会决定这样做。
相反,您可以创建一个Dictionary<switchType,Delegate>,用成对的值操作填充它,然后选择适当的操作,O(1)因为字典是一个哈希表。
dictionary[value].Invoke().

于 2013-02-28T15:46:24.460 回答