如何在LINQ语句中使用DateTime.TryParseExact

时间:2014-03-05 21:09:30

标签: .net vb.net linq tryparse

鉴于以下源数据:

Dim inputs = {New With {.Name="Bob", .Date="201405030500"},
              New With {.Name="Sally", .Date="201412302330"},
              New With {.Name="Invalid", .Date="201430300000"}}

我编写了以下LINQ查询(目的是在Where success之前添加Select子句作为过滤器,但我已将其删除并包含success用于调试目的的结果):

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
              }

但是,我得到了以下结果,newDate未填充TryParseExact

Results without a parsed Date

因此,我重构了查询以将TryParseExact提取为匿名方法以获取以下内容:

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
              }

......这正确地给了我:

Results with a parsed Date

但是,我想找到一种完全在LINQ语句中完成此操作的方法,而无需使用匿名方法来创建元组。当然,我有一些有用的东西,但我想这样做是为了学术兴趣。

我怀疑这可能是可能的,但我的问题是:

为什么result1查询没有使用解析日期正确设置newDate

(我考虑过懒惰的评价,但我不认为它适用于此。)

更新

  • 感谢Hamlet告诉我你实际上可以同时声明和调用匿名方法!心灵=吹嘘!
  • 感谢Jim建议Nullable而不是Tuple!很有意义。
  • 感谢Ahmad建议关闭,这对我来说当然比匿名方法更清晰。

由于下面的答案,我按如下方式重构了LINQ(包括Where子句过滤器):

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
              }

Results with a parsed Date

这实现了我正在寻找的东西,并且我相信编译器可以优化生成的代码,但我仍然想要明白为什么result1不起作用。也许这对Eric Lippert或John Skeet来说是一个问题。

3 个答案:

答案 0 :(得分:2)

您可以在查询之外声明newDate,而不是在Let子句中声明newDate。然后你的查询会像你原来那样引用它。

Dim newDate As New DateTime
Dim query = From i in inputs
            Let success = DateTime.TryParseExact(i.Date, "yyyyMMddHHmm", Globalization.CultureInfo.InvariantCulture, Globalization.DateTimeStyles.AssumeLocal, newDate)
            Select New With {
              .Name = i.Name, 
              .Success = success,
              .Date = newDate
          }

对于它的价值,在C#中我们需要使用out关键字作为DateTime.TryParseExact方法。尝试将newDatelet子句一起使用,类似于第一个示例,将导致C#编译器生成错误:

  

无法将范围变量'newDate'作为out或ref参数传递

答案 1 :(得分:2)

我建议您使用TryParsers库,这是专为在LINQ查询中使用TryParse方法而创建的。

Dim query = From i In inputs
    Let d = TryParsers.TryParse.DateTimeExact(i.Date, "yyyyMMddHHmm", Globalization.CultureInfo.InvariantCulture, Globalization.DateTimeStyles.AssumeLocal)
    Select New With {
          .Name = i.Name,
          .Success = Not d Is Nothing,
          .Date = d.Value
    }

您不能使用范围变量(let)作为参考/输出参数,因为let关键字会创建一个只读变量(实际上它会编译为匿名对象的属性) )。

答案 2 :(得分:1)

您可能需要考虑使用Nullable Date作为.Date属性,并让.Success成为DateTime?.HasValue的评估。然后,您需要修改parseDate lambda以返回可为空的nullable,如下所示:

Dim inputs = {New With {.Name="Bob", .Date="201405030500"},
              New With {.Name="Sally", .Date="201412302330"},
              New With {.Name="Invalid", .Date="201430300000"}}

Dim parseDate = Function(d As String)
                    Dim parsedDate As DateTime? = DateTime.Now
                    If DateTime.TryParseExact(d, "yyyyMMddHHmm", Globalization.CultureInfo.InvariantCulture, Globalization.DateTimeStyles.AssumeLocal, parsedDate) Then
                        Return parsedDate
                    Else
                        Return Nothing
                    End If
                End Function

Dim result2 = From i in inputs
              Select New With {
                  .Name = i.Name,
                  .Date = parseDate(i.Date)
              }

我实际上会将parseDate从lambda移到静态帮助器方法,以便您可以更轻松地在代码中的其他地方重用它。

Public Shared Function parseDate(d as String) as DateTime?
   Dim parsedDate As DateTime? = DateTime.Now
   If DateTime.TryParseExact(d, "yyyyMMddHHmm", Globalization.CultureInfo.InvariantCulture, Globalization.DateTimeStyles.AssumeLocal, parsedDate) Then
       Return parsedDate
   Else
       Return Nothing
   End If
End Function