C# has made the switch statement no longer suck
You heard me right folks, I've raged on about how terrible it is to use the humble switch statement ad nauseum, but since using some improvements in C# I have to admit it's not only less sucky but actually quite good!!talk about why switch is bad, so very very bad. explain that it comes from the world of not only C function pointer tables but all the way back to indirect vector addressing and the XLAT command in 8086 mnemonics, of course 68000 series have their equivalent. explain that it makes code that should have no such space limitations look very messy indeed, less readable and just well...unprofessional. Then explain the many improvements that have been made both by Java and C#, but explain that C# goes just that little bit further with its lovely mapping style, and show some examples of why it makes life that much nicer.
Switch is bad because it hides programming intent and can be prone to simple mistakes. in java the old switch could be written as:
enum EncType { SHA1, SHA2, RSA, PLAINTEXT }
private final String ShhhhItsASecret(final String msg, final EncType encryptionType) {
String result = "";
switch(encryptionType) {
case SHA1:
result = String.format("encrypted sha1(%s)", msg);
break;
case SHA2:
result = String.format("encrypted sha2(%s)", msg);
break;
case RSA:
result = String.format("encrypted rsa(%s)", msg);
case PLAINTEXT:
result = String.format("%s", msg);
break;
default:
result = "not sure what to do with your msg";
}
}
So let's test that theory
System.out.println(ShhhhItsASecret(EncType.SHA1, "my-password"));
System.out.println(ShhhhItsASecret(EncType.SHA2, "my-password"));
System.out.println(ShhhhItsASecret(EncType.RSA, "my-password"));
System.out.println(ShhhhItsASecret(EncType.PLAINTEXT, "my-password"));
result:
encrypted sha1(my-password)
encrypted sha2(my-password)
my-password
my-password
ooops, so it looks like RSA is no more secure than plain text here D'oh!
I realise it's a silly mistake but it's a very easy one to make and if we
haven't been good little programmers and written our unit tests properly
this sort of thing could potentially go unnoticed!
In this sort of circumstance I would always advocate a sort of command
dictionary such as:
import java.util.AbstractMap.SimpleEntry;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import static rljassociates.co.uk.blog.SwitchStatementBlog.EncType.PLAINTEXT;
import static rljassociates.co.uk.blog.SwitchStatementBlog.EncType.RSA;
import static rljassociates.co.uk.blog.SwitchStatementBlog.EncType.SHA1;
import static rljassociates.co.uk.blog.SwitchStatementBlog.EncType.SHA2;
public class SwitchStatementBlog {
public enum EncType {
SHA1, SHA2, RSA, PLAINTEXT
}
@FunctionalInterface
interface Encryptor {
String encrypt( String msg );
}
Map<EncType,Encryptor> commandDictionary = new HashMap<>(
Arrays.asList(
new SimpleEntry<EncType,Encryptor>( SHA1, (msg) -> String.format("encrypted sha1(%s)", msg) ),
new SimpleEntry<EncType,Encryptor>( SHA2, (msg) -> String.format("encrypted sha2(%s)", msg) ),
new SimpleEntry<EncType,Encryptor>( RSA, (msg) -> String.format("encrypted rsa(%s)", msg) ),
new SimpleEntry<EncType,Encryptor>( PLAINTEXT, (msg) -> msg )
).stream().collect( Collectors.toMap( Entry::getKey, Entry::getValue ))
);
// the command dictionary doesn't need to be defined anywhere near where it's used
// it could be created via some config file, not so easy to do with the switch
private String ShhhhItsASecret(final String msg, final EncType encryptionType) {
return commandDictionary.getOrDefault( encryptionType, (__) -> "not sure what to do with your msg" )
.encrypt( msg );
}
}
Not only does this reduce the amount of code but also provides more of an
explanation about what decision we're making and why, but also provides a
very simple way to extend the idea should we require more commands to follow
There's nothing new about the switch statement or a command dictionary, back when I was using early C code this was simply some form of indexed collection of function pointers.
char* sha1(char* msg) { return "sha1"; }
char* sha2(char* msg) { return "sha2"; }
char* rsa(char* msg) { return "rsa"; }
char* plain(char* msg) { return "plain"; }
const int SHA1 = 0;
const int SHA2 = 1;
const int RSA = 2;
const int PLAIN = 3;
(char*(char*))* commands[] = {
&sha1, &sha2, &rsa, &plain
};
char* shhhSecret(const int type, const char* msg) {
return commands[type](msg);
}
It has been so long since I looked at C I don't even know if this is correct but it does show that a command dictionary is simple to construct and has been arround for a long time, I even discovered a similar technique using the XLAT operation --- actually not sure XLAT is correct here!!