If statement can't change local variable - c#

Why can't i change value with an if statement?
int amount;
string inputbalk = inputbar.Text;
if (inputbalk== string.Empty)
{
amount = Convert.ToInt32(inputbalk);
amount = 1;
}
if (inputbalk != string.Empty)
{
amount = Convert.ToInt32(inputbalk);
amount = 1;
}
int onepercent = amount/= 100;
It will see "amount" as an unassigned variable, even though I set it to 1, by two different if-statements.
If I debug, I get this:
"Error 1 Use of unassigned local variable 'amount'"
Thanks for help guys, It is fixed/solved.

Just change second if to else
if (inputbalk== string.Empty)
{
amount = Convert.ToInt32(inputbalk);
amount = 1;
}
else
{
amount = Convert.ToInt32(inputbalk);
amount = 1;
}
The compiler can't make sure that one of two if statements will work anyway, so it will throw error that your variable amount can be unassigned.
In if/else notation one of two code blocks will be done anyway, so compiler will not throw error and everything will work as you want.

There is chance of having inputbalk as null :)

if is not a loop
considering inputbalk is string, it can be a null, you don't check for it, so could happen that it's your case.
Change your code, like this:
if (string.IsNullOrEmpty(inputbalk))
{
amount = Convert.ToInt32(inputbalk);
amount = 1;
}
else
{
amount = Convert.ToInt32(inputbalk);
amount = 1;
}
Hope this helps.

The problem is that the compiler cannot see that amount is definitely assigned. You know that inputBalk can't change between the first and second time it is evaluated, and that exactly one of the if conditions will be true, but the compiler doesn't check this. It sees only that there is a path where amount isn't assigned before it is used, which is disallowed.
You should use if/else instead of testing the same condition twice.
if (...)
{
amount = 1;
}
else
{
amount = 2;
}
Now the compiler can see that every possible path through the code causes amount to be definitely assigned.
There are also a number of other advantages: the code is more concise, easier to read and more maintainable. Furthermore, there is a slight performance benefit from not doing the same check twice.

You could however also encounter an FormatException with the Convert.ToInt32() method. Consider using int.tryParse as an alternative.

Because the chances are that you won't go into any if statement and thus leaving the amount as unassigned. You will get error on this line:
int onepercent = amount /= 100;
At compile time, compiler will not be able to determine whether any of the if statement will result in true and setting of amount. To avoid this error you could do (at start) :
int amount=-1;
Now you will not get your compiler error and value of the amount will change in the if statement. P.S. ('If' is a statement not a loop)

Give this a blast:
if (!int.TryParse(inputbar.Text, out amount))
amount = 1;

Related

Does it make a difference when I return the value from a method?

This is my code currently - I'm just iterating through some lists and returning strings.
public string CreateCharacterName (int category) {
string name = "";
if(category == 0) {
int r = Random.Range(0, Names(Roots("Characters", "CharacterNames"), 0).Count);
name = Names(Roots("Characters", "CharacterNames"), 0)[r];
}
return name;
}
I've cut out a bunch of similar if statements, but I'm wondering if I should put the 'return name' part into each of the if statements after I've got the result, or is it fine as it is? Both work, I'm just thinking that the method will continue checking each subsequent if statement against the 'category' and THEN return the string, is that correct and is there a cost to that?
Thanks!
It's fine as it is. Your cyclomatic complexity is 2, i.e there is only 1 binary path through your code, which is good. Moving the return name; statement would increase it a little, which isn't bad though. As a rule of thumb, try to reduce complexity.
if microsecond optimisation is important why not skip using the variable and just use:
return Names(Roots("Characters", "CharacterNames"), 0)[r];

Using random rnd inside a method causes multiple errors

Having problem with the method down below. The current error I get is "it does not return any value" If I put the return num outside of the brackets, it does not exist in the current context.
If I remove the brackets all together I get:
Embedded statement cannot be a declaration or labeled statement
The name 'rnd' does not exist in the current context
I've done multiple methods before without this issue. What am I missing?
public static int generateNumber(int timesUserWantsToGuess)
{
for (int i = 0; i <= timesUserWantsToGuess; i++)
{
Random rnd = new Random();
int num = rnd.Next(1, 50);
return num;
}
}
First off you have a mismatch of semantics. You are returning a single int, but are executing and returning within a loop.
If you actually wanted to return a collection of ints, you could do something like this:
public static IEnumerable<int> generateNumber(int timesUserWantsToGuess)
{
//Optional, but good error checking
if (timesUserWantsToGuess <= 0)
throw new ArgumentException("Invalid guess count");
Random rnd = new Random();
for (int i = 0; i < timesUserWantsToGuess; i++)
{
int num = rnd.Next(1, 50);
yield return num;
}
}
Note the use of yield to return the next object in the sequence. You also shouldn't instantiate Random in a tight loop, since it will cause the same values to be generated (same seed). Also, using <= in the for conditional will yield one more iteration than the passed value, so I switched to <.
If you didn't want a loop at all, then just remove it and your problem will be solved. At the moment, the loop semantic and the rest of your code mismatch, which is causing you the problem.
The issue is that since the return statement exists inside a loop, the compiler assumes there's a chance it could execute without ever hitting the block, and thus won't return. This could logically happen if timesUserWantsToGuess is less than 0 - the loop never enters in this case. The reason this code compiles if the return statement is outside the block is that by being outside the block, the execution guarantees it'll hit the return (or throw an exception) at some point.
If you remove the brackets on a block in C# (if, for, foreach, lock, and while all do this to my knowledge) you are saying that the block contains only the next line (which is indented by convention) and nothing more. Since variable declaration on a one-line ("embedded") statement can't be used anywhere else syntactically, it doesn't make sense to do it, so the compiler forbids it altogether. Be noted it is also possible to declare a variable in a block without using it, as long as the block is bracketed.
Finally, there's a huge semantic mismatch in the logic of returning a single value during a loop like this: see #BradleyDotNET's answer on yield return statements

Unassigned variable in return statement

for some reason I don't seem to be able to put the return in a fashion that captures this stored procedure's return value (0 or -1), either it returns the value which was used to initialize the variable or keeps telling me the local variable is unassigned.
public int changePass(int idUsuario, String old_pass, String new_pass)
{
int result;
try
{
DataTable tablaResultado =
DataBaseAccess.advanceStoredProcedureRequest(
"pa_usuario_change_pass",
new SPP[]
{
new SPP("id_usuario", idUsuario.ToString()),
new SPP("old_pass", old_pass.ToString()),
new SPP("new_pass", new_pass.ToString())
});
if (tablaResultado.Rows.Count > 0)
{
if (tablaResultado.Rows[0] != null)
{
result = (int.Parse(tablaResultado.Rows[0].ItemArray[0].ToString()));
}
}
return result;
}
catch (System.Data.SqlClient.SqlException sqlException)
{
throw sqlException;
}
}
I have multiple methods which follow the same pattern and all of those works, I have been trying to get this to work for awhile now, and I'm sure I'm overlooking something very, very obvious. Either way I cannot find it right now so I was hoping someone could refresh my memory or give me a tip.
The code only assigns a value to the result variable if two different conditions both happen to be true. That is, if the row count is > 0 and the first row is non-null. The compiler is telling you, completely legitimately, that your code contains a path to the return statement where the variable being used hasn't been assigned a value yet.
You need to decide what the method should return if either of those conditions are not true, and then assign that value to the result variable as part of its initialization.
EDIT:
To be clear: it seems you may be overlooking the possibility that your code won't successfully execute the stored procedure. But it can only return a value to be assigned to result when those two conditions are true. You either need to pick a default value that is reasonable to return when one or the other of the conditions aren't true, or you need to fix the code so that those conditions are always both true (and so the SP always executes successfully).

Variable scope in c#: Not working as expected

Consider this code
class Program
{
static void Main(string[] args)
{
string str;
int x;
for (x = 1; x < 10; x++)
{
str = "this";
}
Console.WriteLine(str);
Console.ReadLine();
}
}
When i compile i get: Error Use of unassigned local variable 'str' (I understand this part)
If i change for loop to if then it works fine. why so (confused here) ??
class Program
{
static void Main(string[] args)
{
string str;
int x;
if (true)
{
str = "this";
}
Console.WriteLine(str);
Console.ReadLine();
}
}
What is the reason for this different behavior?. I expected it should give same result in both situations.
What am i doing wrong ?
With static analysis, the compiler knows for sure that your if statement will run and that str will be assigned.
Change your 2nd example to
class Program
{
static void Main(string[] args)
{
string str;
int x;
bool b = true; // With "const bool" it would work, though
if (b)
{
str = "this";
}
Console.WriteLine(str);
Console.ReadLine();
}
}
and you will have the same behavior as the for loop.
The compiler doesn't know for sure that your for loop will be executed even if you know it will be, so that's why it's telling you about the unassigned variable. A more sophisticated compiler might be able to see that your variable is fine in this case, but handling all such cases is a very complicated problem.
If x was a constant (which makes no sense in a for loop since you want to increment it...) the compiler would be able to see that 1 is indeed smaller than 10 and it wouldn't warn you about the unused variable. Of course the loop would run forever now, but I'm saying this just to highlight that the compiler can only be sure about constants.
The reason is that in the first case the compiler considers a case where loop is never executed:
for (x = 1; x < 10; x++)
{
str = "this";
}
So it assumes that str may stay uninitialized.
In the second case, the condition is always true, so compiler considers that str is always initialized:
if (true)
{
str = "this";
}
The compiler cannot be certain that a for loop will actually iterate. It could loop 0 times. An if(true) statement is known to the compiler to unconditionally execute.
While, in theory, a suitably advanced compiler could reason that the first code block does in fact unconditionally execute, solving the problem in the general case is impossible (you run into the Halting Problem). The compiler is forced to use heuristics to take a reasonable guess at whether or not a given statement is reachable. If it states that a path is not reachable you can know with certain that it is not reachable. If it says that it is reachable it may be reachable, or it may be a false positive.

Local variable and Try/Catch

I get an error, a red line below the variable intAge in the if-statement in the code. It says the variable is local, but how could it be local when it is declared in the beginning of the code? Does it have to do with the Try/Catch part? The reason why my code looks like it does, is just beacuse I have to use a Try/Catch in the code for this task. Preciate some suggestions to solve this in a similiar and correct way? Thanks!
int intAge;
try
{
intAge = int.Parse(age);
}
catch (Exception)
{
MessageBox.Show("Enter an age in numbers!","Error!");
}
finally
{
}
// Check input
if (intAge < 1)
{
inputOk = false;
errorMessage = "Please enter 1 or higher!";
}
just initialize the intAge:
int intAge = 0;
You are getting error use of unassigned local variable.
Since you are assigning the value in the try block, the compiler can't determine if the assignment will take place or not (in case if int.Parse(age) throws an exception), and then in your check if(intAge<1) you are getting the error because you are using a variable not previously assigned.
Definite assignment - MSDN
At a given location in the executable code of a function member, a
variable is said to be definitely assigned if the compiler can prove,
by static flow analysis, that the variable has been automatically
initialized or has been the target of at least one assignment.
If int.Parse fails, the intAge variable will not be initialized.
You may initialize it at delecration
int intAge = 0;
You may avoid the double error :
int intAge;
if (!int.TryParse(age, out intAge))
{
inputOk = false;
errorMessage = "Enter an age in numbers!";
}
else
{
// Check input
if (intAge < 1)
{
inputOk = false;
errorMessage = "Please enter 1 or higher!";
}
}
It's local because it's declared in the local scope. Your code may be better structured like:
int intAge;
if (!int.TryParse(age, out intAge))
{
MessageBox.Show(...
}
else
{
if (intAge < 1)
{
inputOk = false;
errorMessage = "Please enter 1 or higher!";
}
}
With your code above you will display two errors, one for non-numeric, and then one for less than 1. The initial complaint of the compiler was because your integer was not guaranteed to be initialised.
The compiler complains that the local variable intAge might not have been initialized when used for the first time. This may happen when int.Parse(age) throws an exception. To correct this, just initialize intAge to some proper value.
The problem is age is not initialized initialize it to 0 , and try . it must work

Categories

Resources