Reputation: 732
I'm in a scenario where I'm looping through data and formatting it in specific ways based on a setting, and I'm concerned that what I feel is best stylistically might impede performance.
The basic pattern of the code is as follows
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;
...
}
}
My concern is that there might be thousands, or even tens of thousands of items in data. I was wondering if having the switch inside the loop where it may be evaluated repeatedly might cause some serious performance issues. I know I could put the switch
before the loop, but then each case
contains it, which seems much less readable, in that it's less apparent that the basic function remains the same.
Upvotes: 4
Views: 1085
Reputation: 751
If the values in the switch statement are near one to another, the compiler will produce a lookup table instead of N if
statements. It increases performance, but it's hard to say when the compiler will decide to do this.
Instead you can create a Dictionary<switchType,Delegate>
, populate it with pairs of value-action, and then selecting the appropriate action will take about O(1)
as dictionary is a hash table.
dictionary[value].Invoke()
.
Upvotes: 0
Reputation: 460118
You could create a method to keep readability, then pass the data to the method:
void processAllData(IEnumerable<Item> data, setting blah)
{
switch(blah)
{
case blah.single:
foreach (Item item in data)
{
}
}
// next case, next loop ...
}
Then it's just a one-liner:
processAllData(data, blah);
This approach is readable since it encapsulates complexity, concise since you only see what you have to see and efficient since you can optimize the cases.
Upvotes: 2
Reputation: 16137
It certainly does have the potential to affect performance if you're talking about possibly running the comparison tens of thousands of times or more. The other problem that could potentially arise in the code that you've written here is what happens if you need to add to your enum. Then you'd need to open up this code and adjust it to take care of that circumstance, which violates the Open/Closed Principle.
The best way, IMO, to solve both problems at once would be to use a Factory pattern to take care of this (see posts here and here for some advice on starting that). All you'd need to do is have an interface whose implementations would call the method that you'd want to call in your switch code above. Create a factory and have it pick which implementation to return back to your code (before the loop) based on the enum passed in. At that point all your loop needs to do is to call that interface method which will do exactly what you wanted.
Afterwards, any future feature additions will only require you to create another implementation of that interface, and adjust the enum accordingly. No muss, no fuss.
Upvotes: 1
Reputation: 18031
By using a Action delegate this way, you can factorize your code a lot
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;
...
}
Upvotes: 1
Reputation: 109567
It's almost certainly slower to put the switch in the loop like that. Whether it's significant or not is impossible to say - use a Stopwatch to see.
Upvotes: 0
Reputation: 50114
You could set up a delegate/action once, then call it every time in the loop:
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);
}
Basically, put the body of each "case" in an Action
, select that Action
in your switch
outside the loop, and call the Action
in the loop.
Upvotes: 11