|
Nice little concise article (and no disrespect to the commentators supporting simple switc/if's). Although in general, switch and if statements are great for simple linear codeflow, your example demonstrates something that would require logic loops if not using Func<> and Action. Consider the following:
class Programm
{
static void DefaultMethod()
{
Console.WriteLine("Default case");
}
static void LessThan300Method()
{
Console.WriteLine("Smaller than 300");
}
static void Main()
{
Func<int, bool> Default = (x => true);
var cases = new Dictionary<Func<int, bool>, Action>
{
{ x => x < 3 , () => Console.WriteLine("Smaller than 3") } ,
{ x => x < 30 , () => Console.WriteLine("Smaller than 30") } ,
{ x => x < 300 , () => LessThan300Method()},
{ Default, () => DefaultMethod()}
};
int[] fauxWorkflowSteps = {285, 301, 29, 1};
foreach (var fauxWorkflowStep in fauxWorkflowSteps)
{
cases.First(kvp => kvp.Key(fauxWorkflowStep)).Value();
}
Console.ReadKey();
}
}
In the above scenario the simple changes to the base myNum var requires no code branches to represent its output - everything is simply deferred in memory and can be recalled simply by invoking the dictionary with the appropriate key. Imagine this as a simple workflow where the key was a step and each of the actions was a separate method with a parameter for a base class. You then get a feel for why this technique shines over switch statements.
Tomas Ruksenas (http://www.codeproject.com/Messages/4886933/Another-valid-case.aspx[^]) alluded to a similar usecase with his code addition regards parameters changing etc..
However, I do agree horses for courses
Thanks for the article...
modified 28-May-15 4:04am.
|
|
|
|
|
I think it can be valid solution in very specific case. Another possible usage would be when you need to apply multiple conditions in a case.
using System;
using System.Collections.Generic;
using System.Linq;
namespace TestApp
{
class Program
{
static void Main(string[] args)
{
var cases = new Dictionary<Func<int, bool>, string>
{
{ x => x % 3 == 0 , "foo" },
{ x => x % 5 == 0 , "moo" },
{ x => x % 7 == 0 , "coo" },
};
for (int i = 1; i <= 100; i++)
{
Console.Write("Number {0}, Values: ", i);
cases.Where(kvp => kvp.Key(i)).ToList().ForEach(x => Console.Write(x.Value));
Console.WriteLine();
}
}
}
}
|
|
|
|
|
Just because you can do something isn't really justification for doing it
|
|
|
|
|
Just because you are not aware of the existence of a problem, does not mean the problem does not exist.
This is a valid technique and provides a very flexible solution.
Suppose you have a workflow system that is required to perform validation of a data item. Different validation routines are executed based upon some state of the data being processed. Using this technique means
a) You do not have a switch statement with over 100 case statements - very difficult to read.
b) You do not have to include conditional logic in the case statement for each validation routine - very difficult to maintain.
c) You can offload the validation routines and conditional logic into a database where the customer can then update when a validation routine executes as per their requirements - very flexible.
|
|
|
|
|
Yes, you are correct that it is a valid technique (in that it is a valid construct within the language) and that it does provides a very flexible solution. But at what cost? IMO, the readability of the code leaves a good deal to be desired, especially if the person reading the code is not aware of this solution. I've seen too many situations where the people who pick up support of a code base take one look at really clever solutions that they don't understand like that and throw them out, replacing them with something that they do understand.
|
|
|
|
|
You are aware that the compiler optimizes switch statements, right? Although kind of ugly to code, switch statements work pretty fast.
|
|
|
|
|
I think we cant write the condition >= (greater than equal to) with switch.. in that case I use above code snippet
=====================================================
The grass is always greener on the other side of the fence
The difference between genius and stupidity is that genius has its limits." - Albert Einstein
|
|
|
|
|
You are right, you can't use it with conditional expressions. What I meant was to leave a note so that newcomers wouldn't discard the switch statement altogether just because... er... well because it looks kind of ugly. Sometimes it is the right tool to use.
Having said that, I can think of some situations where your snippet is the right tool to crack the nut.
|
|
|
|
|
Thanks for the interesting tip. This may be a more simple approach to the problem.
<br />
int myNumber = 20;<br />
int[] index = { 3, 30, 300 };<br />
int result = index.FirstOrDefault(i => myNumber < i);<br />
string message = result == 0 ? "Nothing found" : string.Format("{0} is smaller than {1}", myNumber, result);<br />
Console.WriteLine(message);<br />
|
|
|
|
|
yes we can
there are many ways to write this snippet
=====================================================
The grass is always greener on the other side of the fence
The difference between genius and stupidity is that genius has its limits." - Albert Einstein
|
|
|
|
|
This is not a good alternative to using a switch statement in these simple cases. The proper alternative is to use an if-else statement. Using a Dictionary of Funcs is too cumbersome and memory intensive. Just because you can do what you have shown and it looks less messy than a switch statement, doesn't make it good programming. The solution is this:
if (x < 3) Console.WriteLine("Smaller than 3");
else if (x < 30) Console.WriteLine("Smaller than 30");
else if (x < 300) Console.WriteLine("Smaller than 300);
else Console.WriteLine("Larger than 299");
|
|
|
|
|
suppose if we want to check value of x more than 1 times
then the if else code will repeat..
I think thats the advantage of the above snippet
=====================================================
The grass is always greener on the other side of the fence
The difference between genius and stupidity is that genius has its limits." - Albert Einstein
|
|
|
|
|
if you need to repeat, just call the procedure again
Relativity
|
|
|
|
|
Sorry, but I disagree. Neither you solution nor mine repeats. If you need it to repeat, put the if-else code in a loop. Your solution is overly complex - simple constructs (if-else and for) will do the same thing and are the better choice.
|
|
|
|
|
Dan, your point of "in these simple cases" is a very important one.
As stated in my comment to ahagel [^], if you are required to execute one (or many) of 100's of validation routines, including these in a switch statement or in a cacophony of if /else statements would make the code almost impossible to maintain.
Not only does this technique allow for the offloading of logic into a database, it also means the customer can configure it to their requirements.
|
|
|
|
|
I see your point John - this technique should not be used to replace simple switches, moderately sized switches, static-logic switches, etc. - because it has a very specific (and to your point, powerful) scenario where it is useful. The author should have explained when this technique is truly useful, instead of stating "This technique is a general alternative to switch, especially if the actions consists only of one line (like a method call)". It is not a general alternative to a switch statement and when used outside of its specific useful scenario, it's like bring a missile to a knife fight. Your comments explaining a good use of this technique are great - and if the author had given a similarly more insightful description, I would likely have given this tip greater consideration. What people write in these articles/tips matters - readers shouldn't have to guess at the specific scenario where this tip would be useful, despite it being called a "general alternative". If he rewrites it, I'll reconsider, but until then, I'm standing by my vote of 1.
|
|
|
|
|
I would like to know why should I use this over a traditional switch statement?
What are the benefits? Are the benefits purely coding cosmetics? or is there a performance / scaling advantage?
Every day, thousands of innocent plants are killed by vegetarians.
Help end the violence EAT BACON
|
|
|
|
|
I'd generally write this as:
if (myNum < 3)
Console.Writeline(...);
else if (myNum < 30)
...
else if (myNum < 30)
...
else
...
A switch would be a bit crazy here. (case 3: case 4: case 5: etc. ... would soon get a bit messy!)
Either way, both of the native constructs would let you do something like:
Console.WriteLine(string.Format("{0} is smaller than 30", myNum);
which wouldn't be possible with this code in its current form.
|
|
|
|
|
I should say that a List is better in this case.
The Dictionary class is optimized to search items by key, yet it uses more memory and is slower than a list to simply add items "at the end". And you use the Dictionary<TKey, TValue> simply as a List<KeyValuePair<TKey, TValue>>, as you use the "First" method to iterate over it.
|
|
|
|
|
I totally agree. I've learned the hard way that a dictionary doesn't preserve the addition order, so in the case of myNum = 12 it can either return the second or third condition.
I like the idea of a runtime configurable switch though!
|
|
|
|
|
I was just going to mention this too
|
|
|
|
|
Althought, since the item should be fixed by compile time, an array would probably be best.
Truth,
James
|
|
|
|
|
Func<int, bool> Default = (x=> true);
var myNum = 1200;
var cases = new Dictionary<Func<int, bool>, Action>
{
{ x => x < 3 , () => Console.WriteLine("Smaler than 3") } ,
{ x => x < 30 , () => Console.WriteLine("Smaler than 30") } ,
{ x => x < 300 , () => Console.WriteLine("Smaler than 300") },
{ Default, () => Console.WriteLine("Something else") }
};
cases.First(kvp => kvp.Key(myNum)).Value();
Truth,
James
|
|
|
|
|
Thanks
Added the default case
=====================================================
The grass is always greener on the other side of the fence
The difference between genius and stupidity is that genius has its limits." - Albert Einstein
|
|
|
|