8

I've just typed in the following code to a VS2015 .Net v4.5.2 console application:

dynamic fromString = "blah", toString = "blah2";
DateTime fromDate, toDate;
if (DateTime.TryParse(fromString.ToString(), out fromDate) && DateTime.TryParse(toString.ToString(), out toDate)) {
    Console.WriteLine(fromDate);
    Console.WriteLine(toDate); 
}

Somewhat unexpectedly I'm getting the error "Use of unassigned local variable toDate". I didn't expected it because the if statement is only entered if 'toDate' is assigned a value from the second TryParse.

Needless to say, it can be worked around by assigning 'toDate' a value:

DateTime fromDate, toDate = DateTime.MinValue;

or changing the && to & so that both TryParses are executed regardless of the first failing.

However, I wonder why the error occurs? If the variables fromString and toString were strings, the error does not occur and the compiler does not give the error that toDate is unassigned. Therefore I wonder why the compiler treats string and dynamic.ToString() differently?

Kate
  • 1,556
  • 1
  • 16
  • 33
  • Well by Microsoft's standards it should be DateTime fromDate, toDate = default(DateTime); ... It's a little known gem, hidden somewhere in their coding standards. – Matthew Layton Mar 17 '16 at 11:59
  • 1
    I've made a bit of progress. First I tried replicating the problem in a console app too and found it didn't reproduce, as some of you found. I then made it closer to the original code and found the issue is with the use of dynamic. In my console app I created two strings: fromString and toString and used them in the TryParse which did not give the error. However, if you change string to dynamic, it now replicates the issue. – Kate Mar 17 '16 at 12:16
  • 1
    @Simon: interesting. Seems to be related: http://stackoverflow.com/questions/2475310/c-sharp-4-0-dynamic-doesnt-set-ref-out-arguments What version of the framework are you using? Why you need `dynamic` at all? – Tim Schmelter Mar 17 '16 at 12:26
  • @TimSchmelter I'm using 4.5.2. – Kate Mar 17 '16 at 12:28
  • 1
    @Simon, check out [similar strange behaviour](https://ideone.com/dStfnb). `(int)p` cast, `&&` -> `&` remove the error. – Ivan Gritsenko Mar 17 '16 at 13:13

2 Answers2

8

This is because you use the short circuit operator &&, which means that if the first TryParse returns false, the second TryParse is never executed thus leaving the ToDate variable unassigned.

Try it, replace && by & and your error will disappear because both TryParse calls will now be always executed.

The compiler is just not clever enough (it doesn't analyse your logic) to know that the code inside won't be executed in some cases.

EDIT: @Simon, I've re-read your question and found that you already knew this... Maybe it's because .ToString always exist on an object but not always on a dynamic (for example when it's a com object), and in that case the compiler does less checks?

Bigjim
  • 2,145
  • 19
  • 19
  • Wouldn't the compiler ignore code in such case? I mean that code is not reachable if either one is false. – Zero Mar 17 '16 at 13:01
  • 2
    It's not very intuitive, but seems to be true. `false && DateTime.TryParse(toString.ToString(), out toDate)` (with first console write removed) does give that error. – Zero Mar 17 '16 at 13:21
  • @Zero But can the compiler prove that the code isn't reachable? That's a lot different than you knowing it's not reachable. – Servy Mar 17 '16 at 13:33
  • @Servy My second comment ended up being misleading. I meant that the answer is Bigjim gave is not very intuitive, but true. – Zero Mar 17 '16 at 13:39
3

This was a breaking change in Roslyn, documented here:

The definite assignment rules implemented by previous compilers for dynamic expressions allowed some cases of code that could result in variables being read that are not definitely assigned. See https://github.com/dotnet/roslyn/issues/4509 for one report of this.

[snip illustrative example]

Because of this possibility the compiler must not allow this program to be compiled if val has no initial value. Previous versions of the compiler (prior to VS2015) allowed this program to compile even if val has no initial value. Roslyn now diagnoses this attempt to read a possibly uninitialized variable.

Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448