Given the following source data:
Dim inputs = {New With {.Name="Bob", .Date="201405030500"},
New With {.Name="Sally", .Date="201412302330"},
New With {.Name="Invalid", .Date="201430300000"}}
I have written the following LINQ query (with the intention of adding a Where success
clause as a filter before the Select
, but I've left it out and have included success
in the results for debugging purposes):
Dim result1 = From i in inputs
Let newDate = New DateTime
Let success = DateTime.TryParseExact(i.Date, "yyyyMMddHHmm", Globalization.CultureInfo.InvariantCulture, Globalization.DateTimeStyles.AssumeLocal, newDate)
Select New With {
.Name = i.Name,
.Success = success,
.Date = newDate
}
However, I get the following results, with the newDate
not getting populated by the TryParseExact
:
So, I refactored the query to extract the TryParseExact
into an anonymous method to get the following:
Dim parseDate = Function(d As String)
Dim parsedDate As DateTime
Return New Tuple(Of Boolean, Date)(DateTime.TryParseExact(d, "yyyyMMddHHmm", Globalization.CultureInfo.InvariantCulture, Globalization.DateTimeStyles.AssumeLocal, parsedDate), parsedDate)
End Function
Dim result2 = From i in inputs
Let parsedDate = parseDate(i.Date)
Select New With {
.Name = i.Name,
.Success = parsedDate.Item1,
.Date = parsedDate.Item2
}
...and this correctly gives me:
However, I'd like to find a way to do this entirely within the LINQ statement without the need to use an anonymous method to create a tuple. Sure, I've got something that works, but I'd like to do this as a point of academic interest.
I suspect this may be possible, but my question is:
Why doesn't the result1
query properly set newDate
with the parsed date?
(I've considered lazy evaluation, but I don't think it applies here.)
Update:
- Thank you Hamlet for showing me that you can actually declare and invoke an anonymous method at the same time! Mind = Blown!
- Thank you Jim for suggesting a Nullable instead of a Tuple! Makes a lot of sense.
- Thank you Ahmad for suggesting the closure, that is certainly cleaner to me than the anonymous method.
Because of the answers below, I've refactored the LINQ as follows (including the Where
clause filter):
Dim result3 = From i in inputs
Let parsedDate = Function(d)
Dim dtParsed As DateTime
Return If(DateTime.TryParseExact(d, "yyyyMMddHHmm", Globalization.CultureInfo.InvariantCulture, Globalization.DateTimeStyles.AssumeLocal, dtParsed), dtParsed, New DateTime?)
End Function(i.Date)
Where parsedDate.HasValue
Select New With {
.Name = i.Name,
.Date = parsedDate.Value
}
This achieves what I'm looking for, and I'm trusting the compiler to optimize the heck out of the generated code, but I'd still like to understand exactly why result1
doesn't work. Perhaps that's a question for Eric Lippert or John Skeet.