2

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:

  1. If currentDataObj is null then booHaveOrigDate is false and the first if fails
  2. If currentDataObj is not null but cant be converted to a DateTime then booHaveOrigDate is false and the first if fails
  3. 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) {
Access IT
  • 95
  • 1
  • 2
  • 9

5 Answers5

6

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

Peter
  • 27,590
  • 8
  • 64
  • 84
  • Thanks for the feedback, what I was illustrating is that the compiler only complains about the variable dteSrc when I change the line to include the "(srcData != null) && " in the line "bool booHaveNewDate = DateTime.TryParse(srcData.ToString(), out dteSrc);". Otherwise it doesnt complain about dteSrc. Sorry the explaination was not clear near the bottom of the original question. – Access IT Oct 26 '11 at 09:07
  • The compiler can't follow your logic. It's just not smart enough to follow all code paths. You can fool it by assigning them first of rewrite your code that it understands your code. The line (srcData != null) makes it impossible for the compiler to see what is going on. – Peter Oct 26 '11 at 09:19
  • Surely this code will just fail if currentDataObj is null - this check being the entire cause of the problem. Missing out the check doesn't really solve much. – Chris Oct 26 '11 at 09:22
  • I had the code like this originally but needed to use the test result of TryParse in code that wasnt shown in the sample. It was only when I changed it to the code I posted that the error showed. It seems it is the addition of the "(currentDataObj != null) &&" before the TryParse that is messing with the compiler parsing – Access IT Oct 26 '11 at 09:23
  • It is my understanding that it will need the null test first to ensure that the .ToString() method doesnt fail in the TryParse call – Access IT Oct 26 '11 at 09:28
  • This is really about code design, working with booleans will make the code even for a human less readable. Which means more bugs. Only use the dates within the context of the if statement and check on null values in srcDate and destData before doing ToString(). – Peter Oct 26 '11 at 09:29
3

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.

H H
  • 263,252
  • 30
  • 330
  • 514
  • But wouldnt the return value of the TryParse handle the case when the conversion fails (rather than just using DateTime.Parse) – Access IT Oct 26 '11 at 09:22
  • Yes with the compliler not evaluating the code it doesnt "understand" that "if (x <= 5) r = 1; if (x > 5) r = 2; " is the same as "if (x <= 5) r = 1; else r = 2;" or "r = x <= 5 ? 1 : 2;" – Access IT Oct 26 '11 at 09:52
1

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

Spencer Ruport
  • 34,865
  • 12
  • 85
  • 147
  • Thanks, some excellent points there. However it seems to be getting tied up on the fact that dteDest can be unassigned because the only time it is used is when the statement "DateTime.TryParse(currentDataObj.ToString(), out dteDest)" which it is an output parameter of is. It gets fooled by adding the null test as part of the logic – Access IT Oct 26 '11 at 09:38
0

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);
Wouter de Kort
  • 39,090
  • 12
  • 84
  • 103
  • 1
    But you can't call `ToString()` on `null`. – H H Oct 26 '11 at 09:09
  • The boolean result of the null test and the DateTime.TryParse is used in the "if (booHaveNewDate && booHaveOrigDate) {" which should mask whether dteSrc or dteDest is unassigned – Access IT Oct 26 '11 at 09:31
  • If currentDataObj is unassigned it will use a string.Empty for TryParse which will return false and set dteDest to MinValue. – Wouter de Kort Oct 26 '11 at 09:41
0

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.

Chris
  • 27,210
  • 6
  • 71
  • 92
  • I understand exactely what you mean and that is very logical. I did update the original quesiton content with a section "Additional Info" which shows an expanded if / else which still fools the compiler though. – Access IT Oct 26 '11 at 09:49
  • Maybe I waffled too much. The essential point is that there is a code path in which dteDest is not set. It doesn't matter about anything else, just that the compiler when looking at all the paths through the code sees one that can cause it to get to a use of an object before it is assigned. It doesn't look at what logic takes it down which paths at all. I can see how you think this is silly, I would be inclined to agree in a simple case like this. However, that is the way the compiler works. If you want to go into details of compiler design I'd suggest asking a separate specific question. – Chris Oct 26 '11 at 10:55