Unassigned Local Variable Error [duplicate] - c#

This question already has answers here:
C# error: Use of unassigned local variable
(4 answers)
Closed 6 years ago.
So I have a folder with only 2 text files, I am reading them and storing the value. This is what the code looks:
public static void UnionFiles()
{
var dinfo =
new DirectoryInfo(
#"\http");
var files = dinfo.GetFiles("*.txt");
int i = 1;
System.Collections.Generic.IEnumerable<String> _eValA, _eValB;
foreach (var file in files)
{
if (i == 1)
{
_eValA = File.ReadLines(file.Name);
++i;
}
else
{
_eValB = File.ReadLines(file.Name);
i = 1;
}
}
IEnumerable<String> union = _eValA.Union(_eValB);
File.WriteAllLines(#"\http\union.txt", union.Cast<String>());
}
But I get this error: Use of unassigned local variable '_eValB, _eValA'
How can I get past it.
Thanks.

The first time through your loop, you will never have both _evalA and _evalB assigned. You need to assign to both to avoid that problem.
There are other related issues. For example, what happens when there aren't exactly two files?
Since there are exactly two files, you shouldn't need to use a loop. If you avoid the loop, you can easily avoid your current issue. For example:
var files = dinfo.GetFiles("*.txt");
System.Collections.Generic.IEnumerable<String> _eValA, _eValB;
// Should really assert that files.Count == 2
_evalA = File.ReadLines(files.First().Name);
_eValB = File.ReadLines(files.Last().Name);
IEnumerable<String> union = _eValA.Union(_eValB);

Think about the possible paths that your code could take. It's possible that _eValA may not be initialized, or that _eValB may not be initialized, and thus you would get an error. Since the compiler can detect this, it gives you a compilation error. You need to make sure they get set equal to a value (or null, which lets the compiler know you're taking responsibility for them, as explained here, but note null wouldn't be appropriate in this case because with 0 or 1 files you'd get an ArgumentNullException at the .Union call) before utilizing them. Try:
var _eValA = new string[0];
var _eValB = new string[0];
foreach (var file in files)
{
if (i == 1)
{
_eValA = File.ReadLines(file.Name);
++i;
}
else
{
_eValB = File.ReadLines(file.Name);
i = 1;
}
}
This will ensure they both get initialized before getting used.

Related

C# compile error Use of unassigned local variable [duplicate]

This question already has answers here:
Why did I get the compile error "Use of unassigned local variable"?
(10 answers)
What does "Use of unassigned local variable" mean?
(11 answers)
Closed 1 year ago.
Error: Use of unassigned local variable 'Datafiles'
I know this question is asked several times, but I don't see anything that suits my requirement. please help!
From the following code to check if files exists, I'm getting the following error, any suggestion on how to fix it, I've already included system.IO on top in namespaces
public void Main()
{
// TODO: Add your code here
string DataFilesLocation;
string[] DataFiles ;
DataFilesLocation = Dts.Variables["User::FolderPath"].Value.ToString();
if (DataFiles.Length > 0)
{
Dts.Variables["User::Flag"].Value = true;
}
Dts.TaskResult = (int)ScriptResults.Success;
}
Thanks fr your help in advance.
You need to assign both variables before you use them, and a best-practice in C# is to assign variables on the line you declare them, if you can.
So it should look something like:
string dataFilesLocation = Dts.Variables["User::FolderPath"].Value.ToString();
string[] dataFiles = System.IO.Directory.GetFiles(dataFilesLocation);
You never assign anything to DataFiles.
Try initializing it with an array size and populating those indexes, or assigning an array to it.
E.G
public void Main()
{
string DataFilesLocation;
// initializes your variable, removing the error you are getting
string[] DataFiles = new string[5];
//populates the array with file names
for(int i=0 ; i< 5 ; i++){
DataFiles[i]="FilePrefix"+i+".png";
}
DataFilesLocation = Dts.Variables["User::FolderPath"].Value.ToString();
if (DataFiles.Length > 0)
{
Dts.Variables["User::Flag"].Value = true;
}
Dts.TaskResult = (int)ScriptResults.Success;
}

C# Conditionally Declare Variables [duplicate]

This question already has an answer here:
Define variable type conditionally C#
(1 answer)
Closed 2 years ago.
I'm trying to declare a variable that is dependent on the bool 'multiline'.
<...some code...>
if (multiline)
{
string[] line;
}
else
{
string line;
}
<...code that uses 'line'...>
But this doesn't work because the declaration of the variables is at the wrong stack level (I think); these variables would only be able to be used within the if/else statements, but I would like to be able to use them outside of the if/else statements.
Is there a way in C# to conditionally create variables?
string[] str;
if (multiline)
{
string[] line = str;
}
else
{
string line = str[0];
}
The short answer is no.
However, I do not believe the question is explained well. Please rethink if you wish to declare a variable dynamically or use its value dynamically.
If the later is true, you can use it in below 2 ways.
Using dynamic
dynamic lines;
if (multiline)
{
lines = new string[10];
}
else
{
lines = "<some string value>";
}
Using array of strings.
string[] lines;
if (multiline)
{
lines = <<string array values>>
}
else
{
lines = "<some string value>";
}
if(lines.Length == 1)
{
// handle single line
}
if(lines.Length > 1)
{
// handle multi line
}
Understand that even if the variables are declared conditionally/dynamically you will have to determine the type at a later point when you wish to handle the variable. My advice would be to bring more details to this question for us to help you better.

Using concatenate to redefine an already defined string

I know a string is immutable and cannot be redefined, but in this foreach loop the string is changed multiple times by adding elements of an array.
var stringOfNames = "";
var arrayOfNames = new string[5] { "jack", "marry", "joe", "jimmy", "bud" };
foreach (var item in arrayOfNames)
{
stringOfNames += item;
Console.WriteLine(stringOfNames);
}
Expected:
An error stating "Variable is already defined in this scope."
Actual:
The string is changed by adding the other names.
Also, what's the difference between these two:
1)
var a = "something";
var a = "something else";
2)
var a = "something";
a+= "asdf";
Why does the second option work?
but in this foreach loop the string is changed
No, it's not.
The variable changes value, to refer to a different string on each iteration. Each of the string objects in question - both the original ones in the array and the intermediate results - stays with the same data that it had before.
Here's another way to demonstrate that:
string x = "ab";
string y = x;
x += "cd";
Console.WriteLine(x); // abcd
Console.WriteLine(y); // ab
Here the value of x changes to refer to a new string, but the value of y still refers to the original string, "ab".
Basically you need to be very clear about three separate concepts:
Variables: named storage locations which store values
References: one kind of value, which refers to an object.
The objects themselves
I have an answer on another question which may help clarify the differences.
stringOfNames += item; does not define the variable stringOfNames, it assigns a value to it.

IndexOutOfRange exception [duplicate]

This question already has answers here:
What is an IndexOutOfRangeException / ArgumentOutOfRangeException and how do I fix it?
(5 answers)
Closed 8 years ago.
Why is this giving a IndexOutOfRange exception?
string[] achCheckStr = File.ReadAllLines("achievements.txt");
if (achCheckStr[0] == ach1_StillBurning) // this is where the exception occurs
{
setAchievements(1);
}
if (achCheckStr[1] == ach2_Faster)
{
setAchievements(2);
}
Problem 1:
there mightbe no file exists with name achievements.txt.
this statement string[] achCheckStr = File.ReadAllLines("achievements.txt"); might be returning null.
Solution 1: so before accessing any files please check whether file exists or not by using File.Exists() method.
Problem 2: there might be no lines in your text file.
Solution 2: before accessing the string array which contains lines please make sure that it is not empty by checking its Length
Try This:
if(File.Exists("achievements.txt"))
{
string[] achCheckStr = File.ReadAllLines("achievements.txt");
if(achCheckStr.Length > 0)
{
if (achCheckStr[0] == ach1_StillBurning)
{
setAchievements(1);
}
if (achCheckStr[1] == ach2_Faster)
{
setAchievements(2);
}
}
}
Your code is assuming that the achCheckStr array has at least 2 elements without first checking how many there are. If the file exists & the contents are empty, achCheckStr.Length will be 0 and the IndexOutOfRangeException will be thrown exactly where it's happening.
Where are you storing "achievements.txt"? It could be in the wrong place, so the code would not find it.
You can fully qualify the path or put the file within the bin directory where the .exe is generated.
Here's a way
string[] achCheckStr = File.ReadAllLines("achievements.txt");
if (achCheckStr != null && achCheckStr.Any())
{
if (achCheckStr[0] == ach1_StillBurning) // this is where the exception occurs
{
setAchievements(1);
}
if (achCheckStr[1] == ach2_Faster)
{
setAchievements(2);
}
}

Should I assign parameter values to local variables first instead of using them directly?

Is there any reason to assign parameter values to local variables inside a method in order to use those values without changing them? I.e. like the following:
private void MyMethod(string path)
{
string myPath = path;
StreamReader mystream = new StreamReader(myPath);
...
}
Or can I always put it like this (and the code above is redundant and just not clean):
private void MyMethod(string path)
{
StreamReader mystream = new StreamReader(path);
...
}
I know it works both ways, but I'd like to be sure there isn't anything I missed in my understanding.
The only time you need to do this (assign locally) is if you are in a foreach loop or using Linq. Otherwise you can run into issues with modified closures.
Here is a snippet from an MSDN blog (All content below is from the link).
http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx
But I'm getting ahead of myself. What's the output of this fragment?
var values = new List<int>() { 100, 110, 120 };
var funcs = new List<Func<int>>();
foreach(var v in values)
funcs.Add( ()=>v );
foreach(var f in funcs)
Console.WriteLine(f());
Most people expect it to be 100 / 110 / 120. It is in fact 120 / 120 / 120. Why?
Because ()=>v means "return the current value of variable v", not "return the value v was back when the delegate was created". Closures close over variables, not over values. And when the methods run, clearly the last value that was assigned to v was 120, so it still has that value.
This is very confusing. The correct way to write the code is:
foreach(var v in values)
{
var v2 = v;
funcs.Add( ()=>v2 );
}
Now what happens? Every time we re-start the loop body, we logically create a fresh new variable v2. Each closure is closed over a different v2, which is only assigned to once, so it always keeps the correct value.
Basically, the problem arises because we specify that the foreach loop is a syntactic sugar for
{
IEnumerator<int> e = ((IEnumerable<int>)values).GetEnumerator();
try
{
int m; // OUTSIDE THE ACTUAL LOOP
while(e.MoveNext())
{
m = (int)(int)e.Current;
funcs.Add(()=>m);
}
}
finally
{
if (e != null) ((IDisposable)e).Dispose();
}
}
If we specified that the expansion was
try
{
while(e.MoveNext())
{
int m; // INSIDE
m = (int)(int)e.Current;
funcs.Add(()=>m);
}
then the code would behave as expected.
It's exactly the same thing, the only difference is that in the first case you make a copy of the reference (which is destroyed anyway when the method gets out of scope, which happens when the execution ends).
For better readability, stick with the second case.
I prefer the second option. It makes no sense to create a new variable with the parameter. Also, from a reading perspective, it makes more sense to create a stream from a path (the one you received) instead of instantiating a "myPath" variable.

Categories