C# 7 Out 变量和模式变量声明不允许在查询子句中

C# 7 Out variable and pattern variable declarations are not allowed within a query clause

为什么查询子句中不允许使用 out 变量?

如果我在这里用完变量就会失败:

string json = "{'PayDays':['2017-07-07','2017-07-21','2017-08-04','2017-08-18']}";
var pd = JsonConvert.DeserializeObject<Accounting>(json);

var rm = from item in pd.PayDays
     where (DateTime.TryParse(item, out DateTime dateresult)) ?
    dateresult.Subtract(DateTime.Now).Days >= 0 ? true : false : false      
    select item;
rm.Dump();

但旧方法有效:

DateTime result;
var rm = from item in pd.PayDays
         where DateTime.TryParse(item, out result) ? (result.Subtract(DateTime.Now).Days >= 0 ? true : false) : false
         select item;
rm.Dump();

问题是您显示的翻译对于正常的 LINQ to objects 工作正常,但对于其他提供程序(如 PLINQ 或 Entity Framework。

会出现问题

例如,考虑您为查询修改为使用 PLINQ 提出的翻译:

DateTime result;
var rm = from item in pd.PayDays.AsParallel()
         where DateTime.TryParse(item, out result) ? (result.Subtract(DateTime.Now).Days >= 0 ? true : false) : false
         select item;
var list = rm.ToList();

此代码不是线程安全的,因为多个线程共享一个 result 变量,您可能会得到不正确的结果,因为它们会同时尝试使用它。

有一些方法可以正确翻译此类查询,至少在某些情况下是这样,但它们并非微不足道,因此 out LINQ 查询语法中根本不允许使用变量。虽然 there is a proposal to allow them in a future version of C#.

这是relevant LDM notes.

结论:

We won't have time to do this feature in C# 7.0. If we want to leave ourselves room to do it in the future, we need to make sure that we don't allow expression variables in query clauses to mean something else today, that would contradict such a future.

The current semantics is that expression variables in query clauses are scoped to only the query clause. That means two subsequent query clauses can use the same name in expression variables, for instance. That is inconsistent with a future that allows those variables to share a scope across query clause boundaries.

Thus, if we want to allow this in the future we have to put in some restrictions in C# 7.0 to protect the design space. We have a couple of options:

  • Disallow expression variables altogether in query clauses
  • Require that all expression variables in a given query expression have different names

The former is a big hammer, but the latter requires a lot of work to get right - and seems at risk for not blocking off everything well enough.

We will neither do expression variables nor deconstruction in C# 7.0, but would like to do them in the future. In order to protect our ability to do this, we will completely disallow expression variables inside query clauses, even though this is quite a big hammer.