Rewrite IsHexString method with RegEx - c#

I got a method that checks if a string is a valid hex string:
public bool IsHex(string value)
{
if (string.IsNullOrEmpty(value) || value.Length % 2 != 0)
return false;
return
value.Substring(0, 2) == "0x" &&
value.Substring(2)
.All(c => (c >= '0' && c <= '9') ||
(c >= 'a' && c <= 'f') ||
(c >= 'A' && c <= 'F'));
}
The rules are:
The expression must be composed of an even number of hexadecimal digits (0-9, A-F, a-f). The characters 0x must be the first two characters in the expression.
I'm sure it can be rewriten in regex in a much cleaner and more efficient way.
Could you help me out with that?

After you updated your question, the new regex that works for you should be:
^0x(?:[0-9A-Fa-f]{2})+$
Where I use (?: for non-capturing grouping for efficiency. The {2} means that you want two of the previous expression (i.e., two hex chars), the + means you want one or more hex characters. Note that this disallows 0x as a valid value.
Efficiency
"Oded" mentioned something about efficiency. I don't know your requirements, so I consider this more an exercise for the mind than anything else. A regex will make leaps as long as the smallest matching regex. For instance, trying my own regex on 10,000 variable input strings of size 50-5000 characters, all correct, it runs in 1.1 seconds.
When I try the following regex:
^0x(?:[0-9A-Fa-f]{32})+(?:[0-9A-Fa-f]{2})+$
it runs about 40% faster, in 0.67 seconds. But be careful. Knowing your input is knowing how to write efficient regexes. For instance, if the regex fails, it will do a lot of back-tracking. If half of my input strings has the incorrect length, the running time explodes to approx 34 seconds, or 3000% (!), for the same input.
It becomes even trickier if most input strings are large. If 99% of your input is of valid length, all are > 4130 chars and only a few are not, writing
^0x(?:[0-9A-Fa-f]{4096})+^0x(?:[0-9A-Fa-f]{32})+(?:[0-9A-Fa-f]{2})+$
is efficient and boosts time even more. However, if many have incorrect length % 2 = 0, this is counter-efficient because of back-tracking.
Finally, if most your strings satisfy the even-number-rule, and only some or many strings contain a wrong character, the speed goes up: the more input that contains a wrong character, the better the performance. That is, because when it finds an invalid character it can immediately break out.
Conclusion: if your input is mixed small, large, wrong character, wrong count your fastest approach would be to use a combination of checking the length of the string (instantaneous in .NET) and use an efficient regex.

So, basically you want to check whether the number starts with 0x and continues with a (non-empty) sequence of 0-9 and/or A-F. That can be specified as a regular expression easily:
return RegEx.IsMatch(value, "^0x[0-9A-Fa-f]+$")
I'm not sure why you do the value.Length % 2 != 0 check... isn't "0x1" a valid hexadecimal number? In addition, my function returns false on "0x", whereas yours would return true. If you want to change that, replace + (= one or many) with * (= zero or many) in the regular expression.
EDIT: Now that you've justified your "even number" requirement, I suggest you use Abel's RegEx. If you do that, I suggest that you call your method IsMsSqlHex or something like this to document that it does not follow the "usual" hex rules.

Diatribe: If you are at all concerned about speed forget about Regex. Regex is a NFA and is as such, in most cases, slower than a DFA or hand-written parser.
Ignoring that you asked for Regex here is something that would likely be more efficient (even though your implementation is probably fine - it does allocate strings):
static bool IsHex(string value)
{
if (string.IsNullOrEmpty(value) || value.Length < 3)
return false;
const byte State_Zero = 0;
const byte State_X = 1;
const byte State_Value = 2;
var state = State_Zero;
for (var i = 0; i < value.Length; i++)
{
switch (value[i])
{
case '0':
{
// Can be used in either Value or Zero.
switch (state)
{
case State_Zero: state = State_X; break;
case State_X: return false;
case State_Value: break;
}
}
break;
case 'X': case 'x':
{
// Only valid in X.
switch (state)
{
case State_Zero: return false;
case State_X: state = State_Value; break;
case State_Value: return false;
}
}
break;
case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
{
// Only valid in Value.
switch (state)
{
case State_Zero: return false;
case State_X: return false;
case State_Value: break;
}
}
break;
default: return false;
}
}
return state == State_Value;
}
If I can garner correctly as to what you are trying to achieve maybe this function will suite your needs better:
static bool ParseNumber(string value, out int result)
{
if (string.IsNullOrEmpty(value))
{
result = 0;
return false;
}
if (value.StartsWith("0x"))
return int.TryParse(value.Substring(2), NumberStyles.AllowHexSpecifier, null, out result);
else
return int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out result);
}
Just for kicks I went and profiled:
Abel's Regex without static readonly caching as I described on Heinzi's answer.
Abel's Regex with static readonly caching.
My implementation.
Results on my laptop (Release/no debugger):
Regex with no compiled/cached Regex took 8137ms (2x cached, 20x hand-written)
Regex with compiled/cached Regex took 3463ms (8x hand-written)
Hand-written took 397ms (1x)

Related

Parsing and looping on user Yes/No console input in C#

I'm writing a method that I will use in another code so I'm writing it separately first.
The code returns a boolean based on the first char of the input (yes, Yes, y, yeah, No, ...). But when the input is different from Y or N it starts acting up. In eg3 It stays in the loop until The Console.Read encounters a Y or an N or there are no chars left. In the last case, it will ask again for input.
Am I using the Console.Read wrong or is there another error in my code?
Thanks in advance
EDIT: Is the solution, in the end, an efficient one?
eg1:
Yes or No?
YeahIthinkso <--my input
True
eg2:
Yes or No?
Negative <--my input
False
eg3:
Yes or No?
Idontknow <--my input
You can only answer with Yes or No
Yes or No?
You can only answer with Yes or No
Yes or No?
You can only answer with Yes or No
Yes or No?
False
The Code:
static void Main(string[] args)
{
char YN = 'x';
bool ans = false;
while (YN == 'x')
{
Console.WriteLine("Yes or No?");
YN = char.ToUpper(Convert.ToChar(Console.Read()));
switch (YN)
{
case 'Y':
ans = true;
break;
case 'N':
ans = false;
break;
default:
Console.WriteLine("You can only answer with Yes or No");
YN = 'x';
break;
}
}
Console.WriteLine(ans);
Alternate solution based on #StuartLC's answer:
bool ans = true, loop = true;
do
{
switch (Console.ReadLine().ToUpper().FirstOrDefault())
{
case 'Y':
ans = true;
loop = false;
break;
case 'N':
ans = false;
loop = false;
break;
default:
Console.WriteLine("You can only answer with Yes or No");
break;
}
} while (loop==true);
Console.WriteLine(ans);
As per #Lasse's comment - if the user types in multiple characters, you'll loop on each character in the string typed in by the user, resulting in the printing a new line of output for each character the user typed. Instead, use of ReadLine will parse the input as a single string, and then the Linq extension FirstOrDefault() will safely obtain the first char in the string:
YN = char.ToUpper(Console.ReadLine().FirstOrDefault());
As an aside, instead of starting a while loop with a forced false condition, C# also supports a do-while loop syntax which fits your requirement better, i.e. at least one iteration through the loop, with a check at the end of the loop:
do
{
Console.WriteLine("Yes or No?");
YN = char.ToUpper(Console.ReadLine().FirstOrDefault());
switch (YN)
{
case 'Y':
ans = true;
break;
case 'N':
ans = false;
break;
default:
Console.WriteLine("You can only answer with Yes or No");
YN = 'x';
break;
}
}
while (YN == 'x');
Re OP's follow up question
Can I now remove the 'YN' completely and put a switch (Console.ReadLine().FirstOrDefault().ToUpper()) in the beginning and a while (ans == true || ans ==false) in the end?
Not quite - since ans is a boolean, it can only have two states (true or false), and you need to model at least 3 states (true, false, and invalid). Although you could use a nullable boolean (bool?) to model the 3 states (where null = invalid), I personally dislike using null to indicate something isn't known, as this invariably leads to the NullReferenceException abyss, and C# hasn't (yet) opted for the "Option" type / Monad (like Java's Optional).
If you're OK with C#8 pattern matching and tuples, you could possibly make the code a bit more concise and refactored as follows, by splitting out the concerns of 'is the input valid' and 'what is the valid input value'?. You could also refactor the switch expression into it's own method to split out the concerns of 'UI' from the parsing logic, which is always a good idea.
static void Main(string[] args)
{
bool ans;
bool isValid;
do
{
Console.WriteLine("Yes or No?");
(ans, isValid) = ParseInput(Console.ReadLine());
if (!isValid)
{
Console.WriteLine("You can only answer with Yes or No");
}
}
while (!isValid);
Console.WriteLine(ans);
(bool ans, bool isValid) ParseInput(string input) =>
char.ToUpper(input.FirstOrDefault()) switch
{
'Y' => (true, true),
'N' => (false, true),
_ => (default, false)
};
}

Not all code paths return a value #2

I am looking to return the bool containsVowel into my main method, where if a word contains a vowel it will allow it to be stored into an array. When I use the code below an error comes up over the method name saying "not all code paths return a value". I've also tried not using the for loop, as well as the char array - it works fine then but if I enter a vowel into the form it still says no vowel has been found.
private bool vowel(string word)
{
bool containsVowel = false;
char[] wordChar = word.ToCharArray();
for (int i = 0; i < word.Length; i++)
{
switch (wordChar[i])
{
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
return containsVowel = true;
break;
default:
MessageBox.Show("Word must contain a vowel", "Error");
return containsVowel = false;
break;
}
}
}
Given your method has the return type bool instead of void, all code paths must return a value. The C# compiler tries to very naively evaluate all code paths to see if your method returns a value from all paths.
Your method looks like this to the compiler:
start
for
switch
case
return
default
return
end
The switch is covered alright thanks to the default, but the for is a conditional branch as well. If you pass an empty string, the for won't be entered (i = 0 and word.Length = 0, so i < word.Length is false from the beginning), so after the for, there needs to be a return statement as well.
You can massively improve the code by doing the following:
public bool ContainsVowel(string word)
{
if (string.IsNullOrEmpty(word))
{
return false;
}
var upperCaseCharacters = word.ToUpper().ToCharArray();
foreach (var character in upperCaseCharacters)
{
switch (character)
{
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
return true;
}
}
return false;
}
This code checks whether the string being passed is null or empty, and then immediately returns false (fail fast). Then it converts the string to upper case once, and loops over the character array with a foreach(), which is the preferred way to iterate over a collection anyway. Then it doesn't use an intermediate variable to store the result, as you can return true as soon as you encounter a vowel.
Finally, when no vowel has been encountered after iterating over all characters, the method returns false.
Then at the call site, you can do this:
if (!ContainsVowel(word))
{
MessageBox.Show("Word must contain a vowel", "Error");
}
Because methods like this should not contain UI logic such as message boxes.
This code should do.First, check for a null word value.Then you must convert your word to upper in order to detect all vowels, being this upper or lower. Then, you must only check for a vowel, because if one is present the method should return true.If,after checking all the letters, the method has not detected a vowel,show error message and return false :
private static bool vowel(string word)
{
if (word == null) return false;
char[] wordChar = word.ToUpper().ToCharArray();
for (int i = 0; i < word.Length; i++)
{
switch (word[i])
{
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
return true;
break;
}
}
Console.WriteLine("Word must contain a vowel");
return false;
}
As you can see, this method always returns a value. In your code, if the word passed is empty,it would not enter the forloop, so it won't return anything.
Your code only checked the first character of the string, since it jumped out of the loop if it was vowel or not. This should fix it, but for a better approach, check #Sr82's answer
private bool vowel(string word)
{
bool containsVowel = false;
char[] wordChar = word.ToCharArray();
for (int i = 0; i < word.Length; i++)
{
switch (wordChar[i])
{
case 'A':
case 'E':
case 'I':
case 'O':
case 'U':
return containsVowel = true;
//if you find a vowel, you can return true straight away
}
}
//if you dont find one, let the for loop finish. If it reaches this code you know
//there was no vowel found and you can proceed with your error report
MessageBox.Show("Word must contain a vowel", "Error");
return containsVowel = false;
}
This simple method should work if you can use Linq. The Intersect method takes two char sequences and returns a sequence of char that exists in both of them. The Any method returns true if there is anything in the sequence.
using System.Linq;
void Main()
{
string s = "Word";
if (hasVowels(s))
{
Console.WriteLine("Word has vowels");
}
else
{
Console.WriteLine("Word does not have vowels");
}
}
//The `Intersect` method takes two `char` sequences and returns a sequence
//of `char` that exists in both of them. The `Any` method returns true if
//there is anything in the resulting sequence.
private bool hasVowels(string word)
{
string vowels = "AEIOUaeiou";
return word != null && word.Intersect(vowels).Any();
}
If you're trying to check whether a string contains a vowel and that's it, you can do something like this:
private bool vowel(string word)
{
return word.ToLower().Any(x => "aeiou".Contains(x));
}
This uses LINQ and specifically the Any method to determine if the string contains any vowels. It would be good to set the string to all lower case so you can check whether a vowel is present irrespective of case.

using a negative number in the default section of a switch statement as a char

I'm not looking for any specific code examples, but could someone explain why I can't get the '-1' to function at the end of this switch statement? It keeps saying that there are "too many literals" for type char. (something close to that). Would I have to convert this to another type?
Thanks for any help, and please, just explain without giving code. I would love to learn this by hands on experiance :D
Convert 7 char passed from ProcessInput() by reference to upper case
Use switch statement to translate char into their corresponding digits (case statement for each digit and each valid uppercase letter)
**TROUBLES WITH THIS PART***Write default case that returns error code (-1) for invalid letters
If no invalide letters, return 0
static void ToDigit(ref char digit)
{
digit = Char.ToUpper(digit);
char result;
switch (digit)
{
case '0': result = '0';
break;
case '1': result = '1';
break;
case '2':
case 'A':
case 'B':
case 'C': result = '2';
break;
case '3':
case 'D':
case 'E':
case 'F': result = '3';
break;
case '4':
case 'G':
case 'H':
case 'I': result = '4';
break;
case '5':
case 'J':
case 'K':
case 'L': result = '5';
break;
case '6':
case 'M':
case 'N':
case 'O': result = '6';
break;
case '7':
case 'P':
case 'Q':
case 'R':
case 'S': result = '7';
break;
case '8':
case 'T':
case 'U':
case 'V': result = '8';
break;
case '9':
case 'W':
case 'X':
case 'Y':
case 'Z': result = '9';
break;
//Says I can't enter -1 as char "too many characters in character literal
default: result = 'e';
break;
}
digit = result;
}
A char is, as the name implies, a single character. A group of characters all "strung together" is called a string in C#.
If you want the integer value -1 as a char then you can do that by saying unchecked((char)(-1)) (*) but you should be aware that this is a very bad idea. I assume that this is your assignment:
Write default case that returns error code (-1) for invalid letters.
That's not how things work in C#; returning a "bad" value to indicate failure is a "worst practice" -- it is characteristic of 1970's style C programming, but not C#.
The right thing to do here is to either (1) have no error cases at all; if there is no upper case form then just don't transform the character at all, or (2) throw an exception if the input is bad, or (3) return a nullable char, and return null for the "bad" value.
Also, the fact that your program takes a ref rather than returning a value is deeply suspicious. A ToDigit method should be computing and returning result not mutating a variable.
I think whatever course of study you are taking was written decades ago, originally targetted a different language entirely, and was never updated to use modern best practices. I would seriously question the value of such materials.
Always say (T)(-1) in C# when casting the constant -1 to the type T, rather than (T)-1. If you write it the latter way, the reader can get confused about whether you mean "subtract one from T" or "cast negative one to type T".
Because '-1' isn't a char, it's two separate characters. '-' and '1'.
Since it talks about returning 0 on success, I assume that the resulting character, and the return value are different things. So they probably want something like this:
static int ToDigit(ref char digit)
{
switch (Char.ToUpperInvariant(digit))
{
case x:
digit=y;
return 0;
...
default:
return -1;
}
}
A few notes:
I'm using ToUpperInvariant instead of ToUpper, since to upper uses the current locale, and that can lead to strange effects. For example your code wouldn't accept an i when run on a Turkish computer.
I'm leaving digit untouched in the error case.
Using int to represent success/error is a bad idea. Should at least be a bool.
You should make result the type int, and return it back to the caller. The caller could then compare it to -1, and then quickly convert back to digit if it's not -1 by adding '0':
int result;
switch (digit) {
// Assign result here
}
char resDigit;
if (result < 0) {
// bad digit
} else {
resDigit = result + '0';
}
As a side note, you can replace your switch with a lookup in a long string of characters:
string lookup = "0 1 2 ABC3 DEF4 GHI5 JKL6 MNO7PQRS8 TUV9WXYZ";
int pos = lookup.IndexOf(char.ToUpper(digit));
if (pos < 0) {
// bad digit
} else {
result = '0' + pos/5;
}
A char can contain only one character, thus '-1' does not work. (char)(-1) is not a valid character value.
My suggestion is to simply use '?' as default (or error) value.

"a constant value is expected" error in switch statement in c#

switch (Rotation.ToString())
{
case (-Math.PI / 2).ToString():
// some code
break;
}
The compiler complains about (-Math.PI / 2).ToString(), "a constant value is expected"
Isn't that a constant?
No method call is a constant value.
Incidentally, since Math.PI is a double, you might want to not use it in "equality" comparisons as you are doing here; there are well-documented pitfalls.
A better approach for a single test would be:
var target = -Math.PI / 2;
if (Math.Abs(Rotation - target) < 0.0001) {
// some code
}
If there are lots of branches, without going into delegate territory, you can do something like this:
var specialCases = new[] { -Math.PI, -Math.PI / 2 };
var epsilon = 0.001d;
var index = specialCases.Select((d, i) => Math.Abs(Rotation - d) < epsilon ? i : -1)
.Where(i => i != -1).DefaultIfEmpty(-1).First();
switch (index) {
case 0: // -Math.PI
break;
case 1: // -Math.PI / 2
break;
default: // No match
break;
}
Not the best use of LINQ, but it will allow you to get back to switch territory with a minimum of tinkering.
Jon answers the gist of the question: the result of a method call is never considered to be a constant from the compiler's perspective. You can do this:
string i = (-Math.PI / 2).ToString();
switch(i)
{
case "-1.5707963267949": Console.WriteLine("hi");
break;
}
However, bear in mind that double.ToString() may produce different results depending on the current culture info. (Hence the reason it cannot be constant.) So you're probably better off using the double value directly and, depending on the size of your switch statement, either creating a few if/else statements, or creating a Dictionary keyed on the constant value you're trying to match.
And of course, Jon's initial warning still holds true: while doing floating-point logic, you could very easily end up with a value that should be -pi/2, but which isn't exactly -pi/2, in which case your check will fail. Here be dragons.
Perhaps try:
const var SomeVar = (-Math.PI / 2).ToString();
switch (Rotation.ToString())
{
case SomeVar:
// some code
break;
}

Multiple cases in switch statement

Is there a way to fall through multiple case statements without stating case value: repeatedly?
I know this works:
switch (value)
{
case 1:
case 2:
case 3:
// Do some stuff
break;
case 4:
case 5:
case 6:
// Do some different stuff
break;
default:
// Default stuff
break;
}
but I'd like to do something like this:
switch (value)
{
case 1,2,3:
// Do something
break;
case 4,5,6:
// Do something
break;
default:
// Do the Default
break;
}
Is this syntax I'm thinking of from a different language, or am I missing something?
I guess this has been already answered. However, I think that you can still mix both options in a syntactically better way by doing:
switch (value)
{
case 1: case 2: case 3:
// Do Something
break;
case 4: case 5: case 6:
// Do Something
break;
default:
// Do Something
break;
}
There is no syntax in C++ nor C# for the second method you mentioned.
There's nothing wrong with your first method. If however you have very big ranges, just use a series of if statements.
Original Answer for C# 7
In C# 7 (available by default in Visual Studio 2017/.NET Framework 4.6.2), range-based switching is now possible with the switch statement and would help with the OP's problem.
Example:
int i = 5;
switch (i)
{
case int n when (n >= 7):
Console.WriteLine($"I am 7 or above: {n}");
break;
case int n when (n >= 4 && n <= 6 ):
Console.WriteLine($"I am between 4 and 6: {n}");
break;
case int n when (n <= 3):
Console.WriteLine($"I am 3 or less: {n}");
break;
}
// Output: I am between 4 and 6: 5
Notes:
The parentheses ( and ) are not required in the when condition, but are used in this example to highlight the comparison(s).
var may also be used in lieu of int. For example: case var n when n >= 7:.
Updated examples for C# 9
switch(myValue)
{
case <= 0:
Console.WriteLine("Less than or equal to 0");
break;
case > 0 and <= 10:
Console.WriteLine("More than 0 but less than or equal to 10");
break;
default:
Console.WriteLine("More than 10");
break;
}
or
var message = myValue switch
{
<= 0 => "Less than or equal to 0",
> 0 and <= 10 => "More than 0 but less than or equal to 10",
_ => "More than 10"
};
Console.WriteLine(message);
This syntax is from the Visual Basic Select...Case Statement:
Dim number As Integer = 8
Select Case number
Case 1 To 5
Debug.WriteLine("Between 1 and 5, inclusive")
' The following is the only Case clause that evaluates to True.
Case 6, 7, 8
Debug.WriteLine("Between 6 and 8, inclusive")
Case Is < 1
Debug.WriteLine("Equal to 9 or 10")
Case Else
Debug.WriteLine("Not between 1 and 10, inclusive")
End Select
You cannot use this syntax in C#. Instead, you must use the syntax from your first example.
With C#9 came the Relational Pattern Matching. This allows us to do:
switch (value)
{
case 1 or 2 or 3:
// Do stuff
break;
case 4 or 5 or 6:
// Do stuff
break;
default:
// Do stuff
break;
}
In deep tutorial of Relational Patter in C#9
Pattern-matching changes for C# 9.0
Relational patterns permit the programmer to express that an input
value must satisfy a relational constraint when compared to a constant
value
You can leave out the newline which gives you:
case 1: case 2: case 3:
break;
but I consider that bad style.
.NET Framework 3.5 has got ranges:
Enumerable.Range from MSDN
you can use it with "contains" and the IF statement, since like someone said the SWITCH statement uses the "==" operator.
Here an example:
int c = 2;
if(Enumerable.Range(0,10).Contains(c))
DoThing();
else if(Enumerable.Range(11,20).Contains(c))
DoAnotherThing();
But I think we can have more fun: since you won't need the return values and this action doesn't take parameters, you can easily use actions!
public static void MySwitchWithEnumerable(int switchcase, int startNumber, int endNumber, Action action)
{
if(Enumerable.Range(startNumber, endNumber).Contains(switchcase))
action();
}
The old example with this new method:
MySwitchWithEnumerable(c, 0, 10, DoThing);
MySwitchWithEnumerable(c, 10, 20, DoAnotherThing);
Since you are passing actions, not values, you should omit the parenthesis, it's very important. If you need function with arguments, just change the type of Action to Action<ParameterType>. If you need return values, use Func<ParameterType, ReturnType>.
In C# 3.0 there is no easy Partial Application to encapsulate the fact the the case parameter is the same, but you create a little helper method (a bit verbose, tho).
public static void MySwitchWithEnumerable(int startNumber, int endNumber, Action action){
MySwitchWithEnumerable(3, startNumber, endNumber, action);
}
Here an example of how new functional imported statement are IMHO more powerful and elegant than the old imperative one.
Here is the complete C# 7 solution...
switch (value)
{
case var s when new[] { 1,2,3 }.Contains(s):
// Do something
break;
case var s when new[] { 4,5,6 }.Contains(s):
// Do something
break;
default:
// Do the default
break;
}
It works with strings too...
switch (mystring)
{
case var s when new[] { "Alpha","Beta","Gamma" }.Contains(s):
// Do something
break;
...
}
The code below won't work:
case 1 | 3 | 5:
// Not working do something
The only way to do this is:
case 1: case 2: case 3:
// Do something
break;
The code you are looking for works in Visual Basic where you easily can put in ranges... in the none option of the switch statement or if else blocks convenient, I'd suggest to, at very extreme point, make .dll with Visual Basic and import back to your C# project.
Note: the switch equivalent in Visual Basic is Select Case.
Another option would be to use a routine. If cases 1-3 all execute the same logic then wrap that logic in a routine and call it for each case. I know this doesn't actually get rid of the case statements, but it does implement good style and keep maintenance to a minimum.....
[Edit] Added alternate implementation to match original question...[/Edit]
switch (x)
{
case 1:
DoSomething();
break;
case 2:
DoSomething();
break;
case 3:
DoSomething();
break;
...
}
private void DoSomething()
{
...
}
Alt
switch (x)
{
case 1:
case 2:
case 3:
DoSomething();
break;
...
}
private void DoSomething()
{
...
}
In C# 7 we now have Pattern Matching so you can do something like:
switch (age)
{
case 50:
ageBlock = "the big five-oh";
break;
case var testAge when (new List<int>()
{ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89 }).Contains(testAge):
ageBlock = "octogenarian";
break;
case var testAge when ((testAge >= 90) & (testAge <= 99)):
ageBlock = "nonagenarian";
break;
case var testAge when (testAge >= 100):
ageBlock = "centenarian";
break;
default:
ageBlock = "just old";
break;
}
One lesser known facet of switch in C# is that it relies on the operator= and since it can be overriden you could have something like this:
string s = foo();
switch (s) {
case "abc": /*...*/ break;
case "def": /*...*/ break;
}
gcc implements an extension to the C language to support sequential ranges:
switch (value)
{
case 1...3:
//Do Something
break;
case 4...6:
//Do Something
break;
default:
//Do the Default
break;
}
Edit: Just noticed the C# tag on the question, so presumably a gcc answer doesn't help.
I think this one is better in C# 7 or above.
switch (value)
{
case var s when new[] { 1,2 }.Contains(s):
// Do something
break;
default:
// Do the default
break;
}
You can also check Range in C# switch case: Switch case: can I use a range instead of a one number
OR
int i = 3;
switch (i)
{
case int n when (n >= 7):
Console.WriteLine($"I am 7 or above: {n}");
break;
case int n when (n >= 4 && n <= 6):
Console.WriteLine($"I am between 4 and 6: {n}");
break;
case int n when (n <= 3):
Console.WriteLine($"I am 3 or less: {n}");
break;
}
Switch case multiple conditions in C#
Or if you want to understand basics of
C# switch case
Actually I don't like the GOTO command too, but it's in official Microsoft materials, and here are all allowed syntaxes.
If the end point of the statement list of a switch section is reachable, a compile-time error occurs. This is known as the "no fall through" rule. The example
switch (i) {
case 0:
CaseZero();
break;
case 1:
CaseOne();
break;
default:
CaseOthers();
break;
}
is valid because no switch section has a reachable end point. Unlike C and C++, execution of a switch section is not permitted to "fall through" to the next switch section, and the example
switch (i) {
case 0:
CaseZero();
case 1:
CaseZeroOrOne();
default:
CaseAny();
}
results in a compile-time error. When execution of a switch section is to be followed by execution of another switch section, an explicit goto case or goto default statement must be used:
switch (i) {
case 0:
CaseZero();
goto case 1;
case 1:
CaseZeroOrOne();
goto default;
default:
CaseAny();
break;
}
Multiple labels are permitted in a switch-section. The example
switch (i) {
case 0:
CaseZero();
break;
case 1:
CaseOne();
break;
case 2:
default:
CaseTwo();
break;
}
I believe in this particular case, the GOTO can be used, and it's actually the only way to fallthrough.
Source
In C# 8.0 you can use the new switch expression syntax which is ideal for your case.
var someOutput = value switch
{
>= 1 and <= 3 => <Do some stuff>,
>= 4 and <= 6 => <Do some different stuff>,
_ => <Default stuff>
};
If you have a very big amount of strings (or any other type) case all doing the same thing, I recommend the use of a string list combined with the string.Contains property.
So if you have a big switch statement like so:
switch (stringValue)
{
case "cat":
case "dog":
case "string3":
...
case "+1000 more string": // Too many string to write a case for all!
// Do something;
case "a lonely case"
// Do something else;
.
.
.
}
You might want to replace it with an if statement like this:
// Define all the similar "case" string in a List
List<string> listString = new List<string>(){ "cat", "dog", "string3", "+1000 more string"};
// Use string.Contains to find what you are looking for
if (listString.Contains(stringValue))
{
// Do something;
}
else
{
// Then go back to a switch statement inside the else for the remaining cases if you really need to
}
This scale well for any number of string cases.
You can also have conditions that are completely different
bool isTrue = true;
switch (isTrue)
{
case bool ifTrue when (ex.Message.Contains("not found")):
case bool ifTrue when (thing.number = 123):
case bool ifTrue when (thing.othernumber != 456):
response.respCode = 5010;
break;
case bool ifTrue when (otherthing.text = "something else"):
response.respCode = 5020;
break;
default:
response.respCode = 5000;
break;
}
An awful lot of work seems to have been put into finding ways to get one of C# least used syntaxes to somehow look better or work better. Personally I find the switch statement is seldom worth using. I would strongly suggest analyzing what data you are testing and the end results you are wanting.
Let us say for example you want to quickly test values in a known range to see if they are prime numbers. You want to avoid having your code do the wasteful calculations and you can find a list of primes in the range you want online. You could use a massive switch statement to compare each value to known prime numbers.
Or you could just create an array map of primes and get immediate results:
bool[] Primes = new bool[] {
false, false, true, true, false, true, false,
true, false, false, false, true, false, true,
false,false,false,true,false,true,false};
private void button1_Click(object sender, EventArgs e) {
int Value = Convert.ToInt32(textBox1.Text);
if ((Value >= 0) && (Value < Primes.Length)) {
bool IsPrime = Primes[Value];
textBox2.Text = IsPrime.ToString();
}
}
Maybe you want to see if a character in a string is hexadecimal. You could use an ungly and somewhat large switch statement.
Or you could use either regular expressions to test the char or use the IndexOf function to search for the char in a string of known hexadecimal letters:
private void textBox2_TextChanged(object sender, EventArgs e) {
try {
textBox1.Text = ("0123456789ABCDEFGabcdefg".IndexOf(textBox2.Text[0]) >= 0).ToString();
} catch {
}
}
Let us say you want to do one of 3 different actions depending on a value that will be the range of 1 to 24. I would suggest using a set of IF statements. And if that became too complex (Or the numbers were larger such as 5 different actions depending on a value in the range of 1 to 90) then use an enum to define the actions and create an array map of the enums. The value would then be used to index into the array map and get the enum of the action you want. Then use either a small set of IF statements or a very simple switch statement to process the resulting enum value.
Also, the nice thing about an array map that converts a range of values into actions is that it can be easily changed by code. With hard wired code you can't easily change behaviour at runtime but with an array map it is easy.
A more beautiful way to handle that
if ([4, 5, 6, 7].indexOf(value) > -1)
//Do something
You can do that for multiple values with the same result
Just to add to the conversation, using .NET 4.6.2 I was also able to do the following.
I tested the code and it did work for me.
You can also do multiple "OR" statements, like below:
switch (value)
{
case string a when a.Contains("text1"):
// Do Something
break;
case string b when b.Contains("text3") || b.Contains("text4") || b.Contains("text5"):
// Do Something else
break;
default:
// Or do this by default
break;
}
You can also check if it matches a value in an array:
string[] statuses = { "text3", "text4", "text5"};
switch (value)
{
case string a when a.Contains("text1"):
// Do Something
break;
case string b when statuses.Contains(value):
// Do Something else
break;
default:
// Or do this by default
break;
}
We can also use this approach to achieve Multiple cases in switch statement... You can use as many conditions as you want using this approach..
int i = 209;
int a = 0;
switch (a = (i>=1 && i<=100) ? 1 : a){
case 1:
System.out.println ("The Number is Between 1 to 100 ==> " + i);
break;
default:
switch (a = (i>100 && i<=200) ? 2 : a) {
case 2:
System.out.println("This Number is Between 101 to 200 ==> " + i);
break;
default:
switch (a = (i>200 && i<=300) ? 3 : a) {
case 3:
System.out.println("This Number is Between 201 to 300 ==> " + i);
break;
default:
// You can make as many conditions as you want;
break;
}
}
}
Using new version of C# I have done in this way
public string GetValue(string name)
{
return name switch
{
var x when name is "test1" || name is "test2" => "finch",
"test2" => somevalue,
_ => name
};
}
For this, you would use a goto statement. Such as:
switch(value){
case 1:
goto case 3;
case 2:
goto case 3;
case 3:
DoCase123();
//This would work too, but I'm not sure if it's slower
case 4:
goto case 5;
case 5:
goto case 6;
case 6:
goto case 7;
case 7:
DoCase4567();
}

Categories