C# error CS0165: Use of unassigned local variable - ignoring logic and out reference
-
13-02-2021 - |
Question
After searching around I cant seem to locate why the C# compiler is complaining that the local variable dteDest is unassigned in the line
if (dteSrc == dteDest) {
The error goes away if I replace the line
DateTime dteSrc, dteDest;
with
DateTime dteSrc, dteDest = DateTime.MinValue;
As far as I can see the code will never reach the comparison line if dteDest is not initialised by the DateTime.TryParse which it is an out parameter for.
My logic is:
- If currentDataObj is null then booHaveOrigDate is false and the first if fails
- If currentDataObj is not null but cant be converted to a DateTime then booHaveOrigDate is false and the first if fails
- DateTime.TryParse will return false if it cant convert to a DateTime this along with the && means that dteDest will never be used.
Simple Sample Code
void StrangeLogic(object srcData, object currentDataObj) {
DateTime dteSrc, dteDest;
bool booHaveNewDate = DateTime.TryParse(srcData.ToString(), out dteSrc);
bool booHaveOrigDate = (currentDataObj != null)
&& DateTime.TryParse(currentDataObj.ToString(), out dteDest);
if (booHaveNewDate && booHaveOrigDate) {
if (dteSrc == dteDest) {
// Get a "use of unassignned local variable 'dteDest'
// unless dteDest = DateTime.MinValue beforehand
}
}
}
Also if I change the line
bool booHaveNewDate = DateTime.TryParse(srcData.ToString(), out dteSrc);
to the following
bool booHaveNewDate = (srcData != null) && DateTime.TryParse(srcData.ToString(), out dteSrc);
then the compiler complains that srcDate is not assigned as well.
Could anyone point me in the right direction to what I am missing - I dont mean about parameter checking etc I am concerned with why the compiler logic seems to be fooled by the use of a common TryParse function?
Additional Info
Even expanding out the logic still gives the same error (use of unassigned local variable)
bool booHaveOrigDate;
if (currentDataObj != null)
booHaveOrigDate = DateTime.TryParse(currentDataObj.ToString(), out dteDest);
else
booHaveOrigDate = false;
if (booHaveOrigDate) {
if (dteSrc == dteDest) {
It appears that it is whatever the compiler does with the null checking (currentDataObj != null) that prevents it from correctly determing the dteDest wont be accessed unless assigned
Change it to this code and no problems (aside from the possible .ToString() on a null object
bool booHaveOrigDate = DateTime.TryParse(currentDataObj.ToString(), out dteDest);
if (booHaveOrigDate) {
if (dteSrc == dteDest) {
Solution
Your replace is incorrect, it should be:
DateTime dteSrc = DateTime.MinValue, dteDest = DateTime.MinValue;
However you should use the return variable of TryParse, which is a bool to see if tryparse worked instead if your booHaveNewDate:
DateTime dteSrc, dteDest;
if(DateTime.TryParse(srcData.ToString(), out dteSrc) && DateTime.TryParse(currentDataObj.ToString(), out dteDest))
{
if (dteSrc == dteDest) {
// Do your stuff here
}
}
Now you do not have to assign the dates in the beginning.
** You should test this code before using, it is no production code and can contain errors
OTHER TIPS
The compiler is formally correct, the assignment to dteDest
(as out
parameter) is conditional. In the eyes of the compiler it might not happen. The compiler doesn't 'understand' the logic that follows from TryParse().
Here is a similar situation:
int f(int x)
{
int r;
if (x <= 5) r = 1;
if (x > 5) r = 2;
return r; // error: use of uninitialized var
}
On a side note, it seems slightly more logical to initialize with
DateTime dteSrc = default(DateTime), dteDest = default(DateTime);
it is the same value (DateTime.MinValue) though.
I could be wrong but I don't think the compiler attempts to dissect your code that extensively when reporting this error. I'm currently trying to find some source to back up my theory. In the mean time, my guess would be that this is a design decision because if it takes a person more than a couple seconds to see that a variable will not be used before being initialized it's probably a better coding decision to just null initialize it to begin with in order to avoid confusion.
EDIT:
Well I did a bit of looking around and while I found a couple examples of people saying essentially the same thing I am I cannot find any official documentation stating this. Here are the responses I found though:
"The compiler is perfectly entitled to not know your logic."
http://www.pcreview.co.uk/forums/use-unassigned-local-variable-error-t3067479.html
"...when there's a control flow structure, it can't evaluate the situation, because it's not executing code, so it doesn't know if the values are getting assigned."
http://bytes.com/topic/c-sharp/answers/917965-why-am-i-getting-unassigned-local-variable-errors
dteDest
will not have a value set if currentDataObj == null
It will work if you change your line to:
bool booHaveOrigDate = DateTime.TryParse(currentDataObj != null ? currentDataObj.ToString() : string.Empty, out dteDest);
compiler logic seems to be fooled by the use of a common TryParse function
The question above can be most easily answered by the fact that when the compiler is compiling your code it doesn't look at what that method is doing internally, it just looks at the signature. It knows it can return a boolean that may be true or false and it knows that it is setting the value of dteDest
.
This isn't really your problem though. The problem comes in that the following line:
bool booHaveOrigDate = (currentDataObj != null)
&& DateTime.TryParse(currentDataObj.ToString(), out dteDest);
Uses the &&
operator which will not evaluate the second part if the first part is false. This is called short circuit evaluation and works on the theory that if the first part is false then it doesn't matter what the second part is - the overall result will always be false.
So in this case dteDest is never being set and the compiler feels this is a problem, even if you look at the logic and say that the code will never run if it is not set.
The simple fact is that people can often spot optimisation and special cases that are beyond the compiler. You sound like you are already aware that you can solve this simply just by checking the parameter at the beginning and then returning if its null.