C# variable scopes and the "switch" statement? - c#

This kind of code would normally work in PHP, but since the scope is much more strict in C#, it's not. I can't figure out a way to write this code without repeating myself.
static double Cube()
{
Console.Write("Enter the side length of the cube: ");
try
{
double x = Convert.ToDouble(Console.Read());
return Math.Pow(x, 3);
}
catch (FormatException)
{
Console.WriteLine("Invalid input, please enter a number.");
Cube();
}
return 1;
}
..Later in Main():
switch (choice)
{
case 0:
return;
case 1:
double final = Cube();
break;
default:
Console.WriteLine("Please enter 0 or 1.");
Main();
break;
}
Console.WriteLine("The volume is: {0}", Convert.ToString(final));
The Cube() method works fine, but it's messy in my opinion (return 1 at the end to make the compiler happy). But an error comes up saying The name 'final' does not exist in the current context. It can't find final. So the only way to make this work that I'm seeing is to put the Console.WriteLine statement right after the double final = Cube().
I've also tried declaring double final; outside the switch, then just setting final inside each case, but that hasn't worked either.
Thanks!

You're right: this is a mess. Start over.
Your fundamental problem is that you're not separating your concerns. You have a method that does user input, input validation, retry logic and math all at the same time. You should rather make methods for each.
Also, use TryParse to handle the failure case, not exception handling.
Finally, recursion is completely the wrong tool to use. A problem must have the following characteristics to be solved by recursion:
A trivial base case.
Can be reduced to a set of smaller problems.
Solutions to smaller problems can be combined to solve larger problems.
Making a problem smaller repeatedly eventually gets to the trivial case.
Your problem has none of these properties, so recursion is automatically the wrong tool. The tool you want is a loop.
static void Main()
{
double x = ObtainDoubleFromUser(
"Enter the side length of the cube: ",
"Please enter a number: ");
Console.WriteLine("The volume is {0}", Cube(x));
}
static double ObtainDoubleFromUser(string firstMessage, string failureMessage)
{
Console.Write(firstMessage);
while(true)
{
double result;
if (Double.TryParse(Console.Read(), out result))
return result;
Console.Write(failureMessage);
}
}
static double Cube(double x)
{
return Math.Pow(x, 3);
}
Does that all make sense? You want to avoid recursion and exception handling if you possibly can. And keep your concerns separated.

If you want to access final from outside the switch scope, you'll have to declare it outside that scope too. If you reference final and there are code paths that allow not setting a value to final, then the compiler will be "angry".
In php, final would magically be 0 when you don't assign anything to it. Try declaring final before the switch, and then assign a value to it at each case statement including the default case.

Place the variable declaration before your switch statement:
double final = 0.0;
switch(choice)
{
...
}
Then just use the variable in your switch statement:
case 1:
final = Cube();
break;
In C#, variables must be declared before they can be used. In your code, the declaration was limited to the scope of the switch statement. Declaring the variable prior to the switch statement ensures that its in the scope of the method, allowing it to be used inside and after the switch statement.

Related

c# Using enum in Switch Case throwing exception

I am working on .NET 6.0 application, I have enum that I am trying to use in switch as to compare with string value but getting exception.
error
private static bool ValidateAlphanumericCase(string text, string fieldName)
{
if (!string.IsNullOrWhiteSpace(fieldName))
{
var rule = GetRule(fieldName).TxtFieldFormat; // string value
switch (rule)
{
case TextFieldFormat.AlphanumericUpperCase.ToString():
break;
case TextFieldFormat.AlphanumericLowerCase.ToString():
break;
}
}
else
{
new EmptyFieldNameException();
}
return false;
}
enum
public enum TextFieldFormat
{
AlphanumericUpperCase = 0,
AlphanumericLowerCase = 1,
}
TextFieldFormat.AlphanumericUpperCase.ToString()
This is a method invocation expression and it is not a valid pattern for swith statement.
You can find all valid patterns here: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns
The closest pattern is type pattern or constant pattern, I guess the compiler recognizes AlphanumericUpperCase as a nested class of TextFieldFormat and fails.
In this case you can use nameof operator.
switch (rule)
{
case nameof(TextFieldFormat.AlphanumericUpperCase):
break;
case nameof(TextFieldFormat.AlphanumericLowerCase):
break;
}
Seems you understood switch-case block a little bit different.
A switch block is simply a shortcut (but more performant shortcut) of many if - else if - else blocks.
Of course they are not the same, but their working logic almost the same.
With this very little info, you can easily think about what's wrong in your code.
Bingo, you're right! Case blocks should check the state. (Boolean value..Just interests with either the given statement results true or false..)
After checking the boolean result, Which case's statement match, code continues on that case block.
So, in your situation your code could be like this :
switch (rule)
{
/// Some statements need to put in paranthesis. Also you would need put business codes of cases into curly braces.
/// I write from my mind.
/// So please try paranthesis and/or braces if this code break.
case rule==TextFieldFormat.AlphanumericUpperCase.ToString():
DoSomethingWithFirstCase(); break;
case rule==TextFieldFormat.AlphanumericLowerCase.ToString():
DoSomethingWitSecondCase(); break;
default: DoSomethingWhenNoMatchWithOtherCases();
}
Hope this helps.
You can also use it this way
TextFieldFormat.AlphanumericUpperCase.ToString("g" or "G")

alternative to global variable for running total

I am very much a novice with C#, but for class I must write a program which keeps a running total of food sales.
We have been taught to use global variables, but not only does it not feel like the easiest way, everywhere I read says not to use them.
Without using a global variable, I have not found a way to keep the cost of two different chocolate bars, for example. It works to get the total cost of 1 bar - regardless of quantity, but when I add another, any previous selections disappear from the total cost.
So in short: what I have works, but I feel that I need to learn a better practice.
Here is a sample of my code (note that there is more than one method which I have not copied into here):
class Program
{
static double total = 0;
static void Main(string[] args)
{
string choice = "y";
while (choice == "y")
{
Console.WriteLine("Main Menu:\nChocolate\nSandwiches");
int menu = int.Parse(Console.ReadLine());
switch (menu)
{
case 1:
chocolate();
break;
case 2:
sandwich();
break;
}
}
}
static void chocolate()
{
int menu = 0;
//double cost = quant * price;
Console.WriteLine("Chocolate bar menu:\nMars bar\nSnickers\nTwix\nMilky Bar\nTurkish Delight");
int chocBar = int.Parse(Console.ReadLine());
Console.Clear();
Console.WriteLine("Quantity");
double quant = double.Parse(Console.ReadLine());
Console.Clear();
if (chocBar == 1)
{
Console.Clear();
double costMars = quant * 0.5;
total = total + costMars;
Console.WriteLine("current total: £" + total.ToString("0.00"));
Console.WriteLine("Press 1 for main menu");
menu = int.Parse(Console.ReadLine());
if (menu == 1)
{
chocolate();
}
else
{
}
}
if (chocBar == 2)
{
double costSnick = quant * 0.8;
total = total + costSnick;
Console.WriteLine("current total: £" + total.ToString("0.00"));
Console.WriteLine("Press 1 for main menu");
menu = int.Parse(Console.ReadLine());
if (menu == 1)
{
chocolate();
}
else
{
}
}
Console.ReadKey();
Congratulations for picking C# as a programming language to learn. It really is a great programming language.
What you read is correct, globals variables have many implications. It is usually last resort. Since you are a beginner I will recommend another simple technique. I found a great link for you that will introduce concepts of passing variables by value and reference. Check it out
https://msdn.microsoft.com/en-us/library/0f66670z.aspx
Good luck.
Good on you for being proactive about learning good coding practices early on in your education! I think your instructor is probably telling you to use globals for right now so as not to overload you with too many concepts at once, but yeah, as a general rule of thumb, you should try to pass variables around your code as method parameters instead. The reason is because if you have a global variable in a large application with tens of thousands of lines of code, and that global variable somehow ends up with an incorrect value in it, then it can be a real nightmare to figure out which piece of code wrote the incorrect value into the variable.
There are a variety of ways to allow data to flow into and out of methods when calling them. The worst way is to use a "global" variable.
Aside: C# technically does not have "global" variables per se, as the term describes variables that are fully accessible from any context, without qualification. All C# variables must be scoped to a type or a method. But, the same general caveats do apply to these kinds of variables. In C#, it is the usage that distinguishes whether a variable is declared along best practices or not, not the declaration itself.
In your code, the most obvious way to approach this would be to have each method return the value to be added to the total.
Also, for some reason, you have implemented your loop as a recursive call. It's not clear to me why you did that, but unless that's a specific requirement in your classroom assignment, I would recommend against it. You're unlikely to run into any specific problems in this context, but it's highly irregular, making the code hard to understand, and giving at least a theoretical possibility of a stack overflow (in reality, you're unlikely to find a user patient enough to cause that to happen, but there's not an actual guarantee in the code that the recursion will eventually terminate).
I am usually hesitant to rewrite homework assignments, but in this case I think you've provided enough code, and advice will be clearer if presented as more code. You still have some work to do, to implement the sandwich() method and the other candy bars, and I've only addressed the immediate problem, not the other flaws in the code. So I don't feel like I'm just doing your school-work for you. :)
Here is the general idea of what I think the code should look like:
class Program
{
static void Main(string[] args)
{
double total = 0;
string choice = "y";
while (choice == "y")
{
Console.WriteLine("Main Menu:\nChocolate\nSandwiches");
int menu = int.Parse(Console.ReadLine());
switch (menu)
{
case 1:
total += chocolate();
break;
case 2:
total += sandwich();
break;
}
Console.WriteLine("current total: £" + total.ToString("0.00"));
Console.WriteLine("Press 1 for main menu");
menu = int.Parse(Console.ReadLine());
}
}
static double chocolate()
{
int menu = 0;
Console.WriteLine("Chocolate bar menu:\nMars bar\nSnickers\nTwix\nMilky Bar\nTurkish Delight");
int chocBar = int.Parse(Console.ReadLine());
Console.Clear();
Console.WriteLine("Quantity");
double quant = double.Parse(Console.ReadLine());
Console.Clear();
double cost;
switch (chocBar)
{
case 1: // Mars
cost = 0.5;
break;
case 2: // Snickers
cost = 0.8;
break;
default:
throw new Exception("Invalid user input");
}
Console.Clear();
return quant * cost;
}
}
Notes:
It's not clear to me what the point of the prompt for "Press 1 for main menu" is. What could the user do at that point except return to the main menu? Whatever your intent, it seems likely that you will do the same thing regardless of whether the user has picked the "Chocolate" or "Sandwiches" option, so I moved that prompt out to the main method.
When dealing with currency, the decimal type is a better choice than double, because human beings don't like the rounding error that occurs with base-2 arithmetic in the double type. Using the decimal type ensures that values entered as decimal remain represented exactly as decimal, eliminating that source of rounding error.
It's not clear to me whether you really intend for the user to be able to purchase a fraction of a candy bar or not. The type you parse the input as is double, which would allow the user to buy e.g. 1.25 candy bars. If that's what you intend, fine. But if not, consider changing the type of the quantity user input to int.
Your original code had no mechanism to break out of the top-level menu, i.e. set choice to something other than "y". I did nothing at all to address that. The same issue still exists in the code here.
"Turkish Delight" may describe a wide variety of candies, but I've never seen one that I'd characterize as a chocolate bar. You might want to reconsider the top-level menu item description, if you're going to sell those. :)

Can I use a variable in a switch statement?

I am coding a text based adventure, and am having a problem. I am trying to make a switch statement case that handles every examination action you want, and am getting this for my code so far:
case "examine" + string x:
//this is a method that I made that makes sure that it is an object in the area
bool iseobj = tut.Check(x);
if (iseobj)
x.examine();
else
Console.WriteLine("That isn't an object to examine");
break;
How do I use a variable in my case statement? I want any string starting with "examine" + (x) to trigger the case.
Your scenario would fit into an if-else statement better than a switch statement. In C#, a switch can only evaluate values, not expressions. This means you can't do:
case input.StartsWith("examine"):
However, you can make this work with an if statement! Consider doing the following:
if (input.StartsWith("examine"))
{
//this is a method that I made that makes sure that it is an object in the area
bool iseobj = tut.Check(x);
if (iseobj)
x.examine();
else
Console.WriteLine("That isn't an object to examine");
}
else if (...) // other branches here

Not all code paths return a value - but they do

The following extract of code is failing to compile resulting in not code paths return a value. Both types Test1StandardChargeCalculator and Test2StandardChargeCalculator are derived from the return type.
I know how to fix this, but my question is why should I have to? A bool is a value type - hence can only represent true or false, both of which are catered for in this snippet. So why the failed compilation?
internal StandardChargeCalculator Create()
{
bool value = true;
switch (value)
{
case true:
return new Test1StandardChargeCalculator();
case false:
return new Test2StandardChargeCalculator();
}
} //not all code paths return a value
When using a switch statement, the compiler does not understand that when you are using a boolean type to switch on there can only be two results.
The error occurs because you do not have a default case.
Don't use a switch for boolean test - use an if statement:
bool value = true;
if(value)
{
return new Test1StandardChargeCalculator();
}
return new Test2StandardChargeCalculator();
Why do you think the compiler should special-case boolean and detect that all possible values have a case statement?
If you were writing a compiler, would you invest development effort and increase the risk of bugs by implementing this?
In Absence of evidence is not evidence of absence Eric Lippert writes about the limitations of 'proofing' that a variable is unassigned and the weaker aim of the compiler in this regard:
that we're not interested in proving for certain that x is unassigned.
We're interested in proving for certain that x is assigned! If we can
prove that for certain, then x is "definitely assigned". If we cannot
prove that for certain then x is "not definitely assigned".
Which does not directly explain this example but note that it is the same issue as :
int x;
if (a < 10)
x = 0;
else if (a >= 10)
x = 1;
y = x; // x is 'unassigned'
We can quickly see that x will always be assigned, the compiler does not even attempt to find out.
To my understanding it is inconsistant to the definition of the switch which says:
If no case expression matches the switch value, then control is transferred to the statement(s) that follow the optional default label. If there is no default label, control is transferred outside the switch.
You are right: there should be no compiler error. So, this might be a case where no answer is the answer. You will have to live with it.
switch (answer)
{
...
default:
return "It is as it is"
}
Maybe better solution would be
internal StandardChargeCalculator Create()
{
StandardChargeCalculator result = null;
bool value = true;
switch (value)
{
case true:
result = new Test1StandardChargeCalculator();
break;
case false:
result = new Test2StandardChargeCalculator();
break;
}
return result;
}

using switch statements with constants or enumerations? (Which is better)? C#

HI, I've got a simple question, but one that has been bugging me for a while.
Question:
When using switch statements in C#, is it considered better practice to use enums over constants or vice versa? Or is it a matter of preference? I ask this because many people seem to like using enums, but when you are switching on an int value, you have to cast each of the values contained in the enum to an int, even if you specify the type of enum.
Code Snippet:
class Program
{
enum UserChoices
{
MenuChoiceOne = 1,
MenuChoiceTwo,
MenuChoiceThree,
MenuChoiceFour,
MenuChoiceFive
}
static void Main()
{
Console.Write("Enter your choice: ");
int someNum = int.Parse(Console.ReadLine());
switch (someNum)
{
case (int)UserChoices.MenuChoiceOne:
Console.WriteLine("You picked the first choice!");
break;
// etc. etc.
}
}
}
Is there some way you can create an instance of the enum and just cast the whole enum to an int?
Thanks!
Why not do this instead?
UserChoices choice = (UserChoices)int.Parse(Console.ReadLine());
switch (choice)
{
case UserChoices.MenuChoiceOne:
// etc...
Then you only need to cast once.
Update: fixed bug in code!
I think the preference of enums over constants is because of readability and not because of performance. I find it easier to read enums in code (in general and not just in switch statements), than to read/understand constants and their usage.
and btw, you don't have to cast every case, you can just cast your switch.
switch((UserChoices)someEnum)
{
...
I believe you can simply do:
switch((UserChoices)someNum)
{
case UserChoices.MenuChoiceOne:
break;
default:
throw Exception // whatever here
}

Categories