C# - Get switch value if in default case - c#

Help please, I have this case:
switch(MyFoo()){
case 0: //...
break;
case 1: //...
break;
case 2: //...
break;
default:
// <HERE>
break;
}
As you can see the switch gets the value directly from a method without saving it as a variable.
Is it possible to get which value fires the default case?
For example if MyFoo() returns 7, how can I get that value?
I want to avoid to save the method result as a variable, is there a way to get the switch value from inside a case? Something like this:
default:
this.SwitchValue // <<--
break;
Thank you for reading,
~Saba

Is there a way to get the switch value from inside a case?
The only (proper) way is actually to store the result of MyFoo() in a variable.
var fooResult = MyFoo();
switch (fooResult)
{
case 0:
...
break;
...
default:
handleOthersCase(fooResult);
break;
}
This code is readable and understandable and have no extra cost (As #SheldonNeilson says: It's on the stack anyway).
Also, the MSDN first example about switch totally look like this. You can also find informations int the language specification.
You also can make your own switch based on a dictionary, but the only advantage I see is that you can use it for complex cases (any kind of object instead of string/int/...). Performance is a drawback.
It may look like this:
public class MySwitch<T> : Dictionary<T, Action<T>>
{
private Action<T> _defaultAction;
public void TryInvoke(T value)
{
Action<T> action;
if (TryGetValue(value, out action))
{
action(value);
}
else
{
var defaultAction = _defaultAction;
if (defaultAction != null)
{
defaultAction(value);
}
}
}
public void SetDefault(Action<T> defaultAction)
{
_defaultAction = defaultAction;
}
}
And be used like this:
var mySwitch = new MySwitch<int>();
mySwitch.Add(1, i => Console.WriteLine("one")); // print "one"
mySwitch.Add(2, i => Console.WriteLine("two")); // print "two"
mySwitch.SetDefault(i => Console.WriteLine("With the digits: {0}", i)); // print any other value with digits.
mySwitch.TryInvoke(42); // Output: "With the digits: 42"
Or based on this response, this:
public class MySwitch2<T>
{
private readonly T _input;
private bool _done = false;
private MySwitch2(T input)
{
_input = input;
}
public MySwitch2<T> On(T input)
{
return new MySwitch2<T>(input);
}
public MySwitch2<T> Case(T caseValue, Action<T> action)
{
if (!_done && Equals(_input, caseValue))
{
_done = true;
action(_input);
}
return this;
}
public void Default(Action<T> action)
{
if (!_done)
{
action(_input);
}
}
}
Can be used like that:
MySwitch2<int>.On(42)
.Case(1, i => Console.WriteLine("one"))
.Case(2, i => Console.WriteLine("two"))
.Default(i => Console.WriteLine("With the digits: {0}", i));

I can't see a reason as well why to use it like that but may be a work around will be like this:
int x;
switch ( x = MyFoo())
{
case 0: //...
break;
case 1: //...
break;
case 2: //...
break;
default:
var s = x; // Access and play with x here
break;
}

No, this isn't possible.
You can assign the value to variable inside switch, if you want to look like reinventing the wheel:
int b;
.....
switch (b = MyFoo())
{
case 1:
break;
case 2:
break;
default:
//do smth with b
break;
}

The easiest way is to save the result of MyFoo() as a variable.. But if you don't want to do that you could do:
switch(MyFoo()){
case 0: //...
break;
case 1: //...
break;
case 2: //...
break;
default:
this.SwitchCase = MyFoo();
break;
}
Although I would advise against this and say save the value as a variable to save your program the extra work.
Saving the value of MyFoo as a variable becomes more important the more complex the example gets as the value of MyFoo could have changed between the switch and default case.
Also this will only work where MyFoo has no side-effects and obviously must always return the same value for any given parameter.
for example the following would work:
Private int MyFoo()
{
return 3;
}
But the following would not:
private int MyFoo()
{
Random r = new Random();
return r.Next(5);
}

This is possible now.
https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8#property-patterns
Example:
int? TryGetColumnIndex(string columnName)
=> GetValue(columnName)
switch
{ var result when result > -1 => result, _ => new int?() };
result will capture the result of GetValue.
Even cooler, you can do propery checks.
i.e instead of when result > -1 you can even say when result.ToString().Length > 2 and such.

Related

C# Case which include other cases

How to execute in one case, other cases? I could just copy paste those other cases, or put it to some external function but there is so much code so I dont want to do that.
Example:
switch(foo)
{
case 3:
{
//something
}break;
case 4:
{
//something else
}break;
case 5:
{
//here i want to execute case 3 and case 4
}break;
}
I think that this was previously answered but I can't find how to do it.
C# doesn't have such functionality. You have to create other methods which will do actions for cases 3 and 4 and call them from case 5 branch. I would suggest to create a separate class FooHandler which would handle your value. It's easily extendable and readable.
public class FooHandler
{
private readonly int _foo;
public FooHandler(int foo)
{
this._foo = foo;
}
public void Handle()
{
switch(this._foo)
{
case 3: this.HandleCase3(); break;
case 4: this.HandleCase4(); break;
case 5: this.HandleCase5(); break;
default: throw new ArgumentException("Foo value is invalid");
}
}
private void HandleCase3()
{
// Your code for case 3
}
private void HandleCase4()
{
// Your code for case 4
}
private void HandleCase5()
{
this.HandleCase3();
this.HandleCase4();
}
}
Usage:
var fooHandler = new FooHandler(foo);
fooHandler.Handle();
If you don't want to add methods (you didn't explain why),
you can use Action local variables holding Lambda expressions.
In the example below you can replace the body of the lambdas with whatever code you have for "something" and "something else".
Action also supports passing arguments to the lambda's body if you need them.
Action something = () => { Console.WriteLine("something"); };
Action something_else = () => { Console.WriteLine("something_else"); };
switch (foo)
{
case 3:
something();
break;
case 4:
something_else();
break;
case 5:
something();
something_else();
break;
}
You could also change the switch to two ifs:
if (foo == 3 || foo == 5)
{
//something
}
if (foo == 4 || foo == 5)
{
//something else
}
It would be easier to use if-statements. Here I also used pattern matching to simplify the tests.
if (foo is 3 or 5) {
// something
}
if (foo is 4 or 5) {
// something else
}
So simple and easy to read and understand.
I would argue that the code being intuitive is important; hence, I would suggest defining helper variables that clarify intention.
While not knowing the meaning of 3, 4 and 5, a hypothetical example could be:
var awesomeFoos = new[] { 3, 5 };
var popularFoos = new[] { 4, 5 };
var fooIsAwesome = awesomeFoos.Contains(foo);
var fooIsPopular = popularFoos.Contains(foo);
if (fooIsAwesome)
{
// something (preferably refactored to a separate method)
}
if (fooIsPopular)
{
// something else (preferably refactored to a separate method)
}
, where .Contains() is found in the System.Linq namespace.
An example fiddle is found here.
That being said, though; you seem quite determined that you would prefer to keep your code as-is, to an as large extent as possible. If that is really a high priority, you could consider putting the whole foo-switch logic inside a method and let it call itself twice in the case 5 scenario:
private static void HandleFoo(int foo)
{
switch(foo)
{
case 3:
{
// something
}break;
case 4:
{
// something else
}break;
case 5:
{
HandleFoo(3);
HandleFoo(4);
}break;
}
}
Example fiddle is found here.
(Depending on the content of // something and // something else, this may not be feasible, though.)
I strongly recommend changing the way you want to implement this statement. This method is not suitable for modern applications and is coupled with everything. But if you need to implement as you asked, You can jump between cases by using goto.
For more information Read "jump statements".
int a = 10;
switch (a)
{
case 0:
//Condition1:
//some actions
break;
case 1:
goto case 0;
//or
goto Condition1;
break;
default:
break;
}
Since this is the linear approach you should check conditions in if for each goto in each case(cause you can't Go back to each step)
Another approach is to save all cases in the order you want to execute and run the switch multiple times. I use a while in my example you can use goto if you don't want to use a loop.
Queue<int> cases = new Queue<int>();
//1 is the main switch value
cases.Enqueue(1);
while (cases.Count > 0)
{
int temp = cases.Dequeue();
switch (temp)
{
case 0:
Console.WriteLine("0");
break;
case 1:
Console.WriteLine("1");
cases.Enqueue(3);//run case 3
cases.Enqueue(0);//then run case 0
break;
case 2:
Console.WriteLine("2");
break;
case 3:
Console.WriteLine("3");
break;
default:
break;
}
}

Pattern Matching on a string

I was wondering if there is a way to do something like this in c# 7
var test = "aaeag";
switch (test)
{
case test.StartsWith("a"):
break;
default:
break;
}
Sadly it does not look like it possible. Is this correct or am I doing something wrong?
This is possible with C# 7, using a when guard:
var test = "aaeag";
switch (test)
{
case var s when s.StartsWith("a"):
break;
default:
break;
}
What your version of the code is doing is often referred to as active patterns. By eg defining the the extension method:
public static bool StartsWithPattern(this string str, string matchPattern) =>
str.StartsWith(matchPattern);
Then your switch could become:
var test = "aaeag";
switch (test)
{
case StartsWith("a"):
break;
default:
break;
}
If you'd like to see this feature in a future C# version, then please upvote this proposal.

Slimming down a switch statement

Wondering if there are good alternatives to this that perform no worse than what I have below? The real switch statement has additional sections for other non-English characters.
Note that I'd love to put multiple case statements per line, but StyleCop doesn't like it and will fail our release build as a result.
var retVal = String.Empty;
switch(valToCheck)
{
case "é":
case "ê":
case "è":
case "ë":
retVal = "e";
break;
case "à":
case "â":
case "ä":
case "å":
retVal = "a";
break;
default:
retVal = "-";
break;
}
The first thing that comes to mind is a Dictionary<char,char>()
(I prefer char instead of strings because you are dealing with chars)
Dictionary<char,char> dict = new Dictionary<char,char>();
dict.Add('å', 'a');
......
then you could remove your entire switch
char retValue;
char testValue = 'å';
if(dict.TryGetValue(testValue, out retValue) == false)
retVal = '-';
Well, start off by doing this transformation.
public class CharacterSanitizer
{
private static Dictionary<string, string> characterMappings = new Dictionary<string, string>();
static CharacterSanitizer()
{
characterMappings.Add("é", "e");
characterMappings.Add("ê", "e");
//...
}
public static string mapCharacter(string input)
{
string output;
if (characterMappings.TryGetValue(input, out output))
{
return output;
}
else
{
return input;
}
}
}
Now you're in the position where the character mappings are part of the data, rather than the code. I've hard coded the values here, but at this point it is simple enough to store the mappings in a file, read in the file and then populate the dictionary accordingly. This way you can not only clean up the code a lot by reducing the case statement to one bit text file (outside of code) but you can modify it without needing to re-compile.
You could make a small range check and look at the ascii values.
Assuming InRange(val, min, max) checks if a number is, yep, in range..
if(InRange(System.Convert.ToInt32(valToCheck),232,235))
return 'e';
else if(InRange(System.Convert.ToInt32(valToCheck),224,229))
return 'a';
This makes the code a little confusing, and depends on the standard used, but perhaps something to consider.
This answer presumes that you are going to apply that switch statement to a string, not just to single characters (though that would also work).
The best approach seems to be the one outlined in this StackOverflow answer.
I adapted it to use LINQ:
var chars = from character in valToCheck.Normalize(NormalizationForm.FormD)
where CharUnicodeInfo.GetUnicodeCategory(character)
!= UnicodeCategory.NonSpacingMark
select character;
return string.Join("", chars).Normalize(NormalizationForm.FormC);
you'll need a using directive for System.Globalization;
Sample input:
string valToCheck = "êéÈöü";
Sample output:
eeEou
Based on Michael Kaplan's RemoveDiacritics(), you could do something like this:
static char RemoveDiacritics(char c)
{
string stFormD = c.ToString().Normalize(NormalizationForm.FormD);
StringBuilder sb = new StringBuilder();
for (int ich = 0; ich < stFormD.Length; ich++)
{
UnicodeCategory uc = CharUnicodeInfo.GetUnicodeCategory(stFormD[ich]);
if (uc != UnicodeCategory.NonSpacingMark)
{
sb.Append(stFormD[ich]);
}
}
return (sb.ToString()[0]);
}
switch(RemoveDiacritics(valToCheck))
{
case 'e':
//...
break;
case 'a':
//...
break;
//...
}
or, potentially even:
retval = RemoveDiacritics(valToCheck);
Use Contains instead of switch.
var retVal = String.Empty;
string es = "éêèë";
if (es.Contains(valToCheck)) retVal = "e";
//etc.

Break tag inside of a method

I have a switch statement that executes some logic over and over. Rather then use cut and paste I wanted to put it into a function, but I am failing badly at this.
This is what I want to do, but it does not compile because the break tag in the function does not exist. Can anyone refactor this to a better working version?
switch(param.ToString())
{
case "1":
BreakIfNotArgumentType<B>(param);
//do stuff
break;
case "2":
BreakIfNotArgumentType<BF>(param);
//do stuff
break;
}
private T BreakIfNotArgumentType<T>(object argumentObject)
{
if (argumentObject is T)
{
return (T)argumentObject;
}
else
{
break;
}
}
Your function is essentially replicating the functionality of the as operator.
string foo = "foo";
....
object a = foo;
object b = 12;
....
string bar = a as string; // will give you "foo"
string zed = b as string; // returns null
The as operator functions as a runtime-safe cast. If the target instance can't be cast to the target type, then a null reference is assigned. Because of this, it will only work with reference types. The most common usage is like this...
string bar = a as string;
if(bar != null)
{
// do stuff
}
Because this gives the type checking and casting operation in one statement.
You should post your switch statement and we may be able to streamline it better. Trying to write a truly inline function as you had (where you were expecting it to substitute the code in the function into your switch statement, which makes it more of a macro than a function) won't work.
You could have the function return a null if the argument can't be cast or throw an exception for a couple of ideas on how to handle that part of the code.
private T BreakIfNotArgumentType<T>(object argumentObject)
{
if (argumentObject is T)
{
return (T)argumentObject;
}
else
{
return null;
}
}
Or
private T BreakIfNotArgumentType<T>(object argumentObject)
{
if (argumentObject is T)
{
return (T)argumentObject;
}
else
{
throw CustomException("Argument wasn't valid!");
}
}
Make your method to return a boolean and check the return value:
switch(param.ToString())
{
case "1":
if (CheckForArgumentType<B>(param))
{
// Do your cast here
param = (B)param
}
else
break;
case "2":
if (CheckForArgumentType<B>(param))
{
// Do your cast here
param = (B)param
}
else
break;
}
.................
private bool CheckForArgumentType<T>(object argumentObject)
{
return (argumentObject is T)
}
if you only have 2 values to compare with - use IF statement instead

Can't cast 2nd Enum value to int?

I don't get this. I was able to cast my first enum value to int but not the second?
public enum PayPalTransactionType
{
Authorization = 0, // Debit
Capture = 1, // Credit
Refund = 2,
Void = 3
}
public string GetPayPalTransCode(PayPalServiceBase.PayPalTransactionType payPalTransactionType)
{
string actionCode = string.Empty;
switch (payPalTransactionType)
{
case (int)PayPalServiceBase.PayPalTransactionType.Authorization:
actionCode = "Debit";
break;
case (int)PayPalServiceBase.PayPalTransactionType.Capture:
actionCode = "Credit";
break;
}
return actionCode;
}
on my 2nd case statement I get this casting error:
Cannot implicitly convert type int
to PayPalTransactionType. An
explicit conversion exists (are you
missing a cast?)
Why are you trying to cast in the first place? Just leave it as the enum value everywhere:
public string GetPayPalTransCode
(PayPalServiceBase.PayPalTransactionType payPalTransactionType)
{
string actionCode = string.Empty;
switch (payPalTransactionType)
{
case PayPalServiceBase.PayPalTransactionType.Authorization:
actionCode = "Debit";
break;
case PayPalServiceBase.PayPalTransactionType.Capture:
actionCode = "Credit";
break;
}
return actionCode;
}
Additionally, I'd have an explicit default action for unrecognised codes, and just return directly:
public string GetPayPalTransCode
(PayPalServiceBase.PayPalTransactionType payPalTransactionType)
{
switch (payPalTransactionType)
{
case PayPalServiceBase.PayPalTransactionType.Authorization:
return "Debit";
case PayPalServiceBase.PayPalTransactionType.Capture:
return "Credit";
default:
return ""; // Or throw an exception if this represents an error
}
}
Alternatively, you could use a Dictionary<PayPalTransactionType, string>.
Why are you casting to int at all? The thing you are switching on is already of the enum type!
As to the other part of the question, the reason the first cast is working is because an implicit cast from a constant int 0 to an enum type always works, whereas the other attempted cast is from a non-zero int value.
why for god's sake are you doing a cast??
public string GetPayPalTransCode(PayPalServiceBase.PayPalTransactionType payPalTransactionType)
{
switch (payPalTransactionType)
{
case (int)PayPalServiceBase.PayPalTransactionType.Authorization:
break;
case (int)PayPalServiceBase.PayPalTransactionType.Capture:
break;
}
}
bascially same type in here, isn't it?! you want to compare enum against enum, don't you?
just do
public string GetPayPalTransCode(PayPalServiceBase.PayPalTransactionType payPalTransactionType)
{
// ...
switch (payPalTransactionType)
{
case PayPalServiceBase.PayPalTransactionType.Authorization:
break;
case PayPalServiceBase.PayPalTransactionType.Capture:
break;
}
// ...
}
btw - it's not best practice to assign PayPalTransactionType.Authorization to 0. 0 should be used for parsing-fallbacks!
edit:
your code would be correct if you do
public string GetPayPalTransCode(PayPalServiceBase.PayPalTransactionType payPalTransactionType)
{
switch ((int)payPalTransactionType)
{
case (int)PayPalServiceBase.PayPalTransactionType.Authorization:
break;
case (int)PayPalServiceBase.PayPalTransactionType.Capture:
break;
}
}
which is quite ...!
Remove the (int) casts in your case statements. The switch can handle the enum values.
Since the switch is on `PayPalTransactionType', values of that type should be used in the case statements.
You don't need these casts in the first place. Just make it case PayPalServiceBase.PayPalTransactionType.Authorization, etc.
Tim,
I am not a Java/C# expert, but here is my 2 cents on the code block above ...
If you're using an enum, why do you need to have an int value, if you do, then do something like:
public enum PayPalTransactionType
{
Authorization(0, Debit),
Capture(1, Credit),
Refund(3, null),
Void(4. null);
private final int code;
public int getCode()
{return(code);}
private PayPalTransactionType(final int code, final TransactionType transactionType)
{
this.code = code;
this.transactionType = transactionType;
}
private TransactionType getTransactionType()
{return(transactionType);}
}
public enum TransactionType
{
Credit("Funds are added to the specified account."),
Debit("funds are deducted from the specified account.");
private final String description;
public String getDescription()
{return(description);}
private TransactionType(final String description)
{this.description = description;}
}
Switches are useful for a very small amount of cases. You can simply do this:
final PayPalTransactionType payPalTransactionType = PayPalTransactionType.Authorization;// this is just an example of the parameter you'd be passing in.
payPalTransactionType.getTransactionType();// an emum is a static database, simply store other things that are related to this value.
Walter
It's not the cast to int that's giving you a problem. Its the cast of (int)PayPalServiceBase.PayPalTransactionType.Capture (which is an int) back to PayPalServiceBase.PayPalTransactionType that's the problem.
You don't need the casts to int. Just use the enum directly.
switch (payPalTransactionType)
{
case PayPalServiceBase.PayPalTransactionType.Authorization:
actionCode = "Debit";
break;
case PayPalServiceBase.PayPalTransactionType.Capture:
actionCode = "Credit";
break;
}
The error occurs because your trying to implicitly convert payPalTransactionType to int. Maybe this will enlighten your problem:
switch ((int)payPalTransactionType)
{
case (int)PayPalServiceBase.PayPalTransactionType.Authorization:
actionCode = "Debit";
break;
case (int)PayPalServiceBase.PayPalTransactionType.Capture:
actionCode = "Credit";
break;
}
This first cast allows you to switch on ints instead of PayPalTransactionTypes. Since the switch statement supports enums, you don't need this cast.
Why are you casting it to an int the first place.? Just do the case statements using the enumerated type.
If you are really just looking up a string a better method would be an array of strings that you index by the enumerated type. That would be faster, less code and easier to maintain.
So something like this:
public enum PayPalTransactionType
{
Authorization = 0, // Debit
Capture = 1, // Credit
Refund = 2,
Void = 3,
Max // This must always be the last type and is used just as a marker.
}
string[] ActionCode = new string[(int)PayPalTransactionType.Max] { "Debit", "Credit", "Refund", "Void" };
public string GetPayPalTransCode(PayPalTransactionType payPalTransactionType)
{
return ActionCode[(int)payPalTransactionType];
}

Categories

Resources