【问题标题】:Initializing var type in LINQ在 LINQ 中初始化 var 类型
【发布时间】:2017-05-12 18:23:19
【问题描述】:

问题:如何在下面的代码中初始化var? [那么,当然,我会从 if 语句中删除 var 声明]

以下代码的最后一行返回众所周知的错误:The name lstCurrent does not exists in current context。显然错误是因为 var 是在 if 语句内定义并在 if 语句

之外使用

注意:我只从表中选择了几列,因此处理的是匿名类型。我在网上看到的一些例子不起作用——可能是因为我的代码选择了匿名类型。但这只是猜测。

var lstCurrent =????;

if(Type==1)
  var lstCurrent =_context.Customers().Where(t =>t.type=="current").Select(c => new { c.LastName, c.City});
else
  lstCurrent  = _context.Customers().Where(...).Select(...)

return View(lstCurrent.ToList());

【问题讨论】:

  • 你声明的变量名是什么?您的代码示例中缺少它...
  • inline code 使用反引号,而不是双星号(粗体)。
  • 您可以使用三元运算符而不是 if 语句。
  • IEnumerable lstCurrent = new List(); ?
  • View()的参数是什么类型?使用那种类型。或者如果那是List<X>,请使用IEnumerable<X>

标签: c# linq


【解决方案1】:

var 不是类型 - 它的意思是“我不关心(或不能)指定类型是什么 - 让编译器为我做这件事”。

在您的情况下,您将两个查询之一的结果分配给它,其中一个返回匿名类型,因此您无法指定类型,因为您不知道匿名类型的名称(因此称为“匿名”)。

为了使用var,编译器在初始化时需要一些表达式来知道实际的类型是什么。

我建议如下:

var lstCurrent = Type==1 
              ? _context.Customers().Where(t =>t.type=="current").Select(c => new { c.LastName, c.City})
              : _context.Customers().Where(...).Select(...)

但请注意,您的“选择”必须返回相同的类型(或具有完全相同字段的匿名类型),否则您将无法使用 var

最后,我会尝试将条件烘焙到您的 Where 子句中,以减少重复代码:

bool isTypeOne = Type==1;

var lstCurrent = _context.Customers()
                         .Where(t => isTypeOne ? t.type=="current" : ...)
                         .Select(c => new { c.LastName, c.City})

【讨论】:

  • 在实际场景中,if(...){...}else if(..){..}else{...} 语句具有三个条件。我在想建议的解决方案也适用于三种情况。我如何将您的建议扩展到三个条件案例?但是,我已将您的回复标记为答复,因为您实际上已经回答了我的问题(基于我的示例)。
  • 你可以链接三元? :操作符:Where(t => checkOne? conditionOne : (checkTwo ? conditionElse : conditionTwo)))
【解决方案2】:

试试

IEnumerable lstCurrent;
if(Type == 1)
    lstCurrent = foo;
else
    lstCurrent = bar;

【讨论】:

  • 使用非泛型 IEnumerable 会阻止 ToList 调用编译。
【解决方案3】:

如何在下面的代码中初始化 var? [那我当然会 从 if 语句中删除 var 声明]

这是不可能的,因为您在左侧分配的对象的类型应该是已知的。比如

var a = "text";

a 的类型在编译时是已知的,因为右手表达式是 string。这不能使用一系列匿名类型来完成,比如您定义的类型。

我可以看到两个选项。一个是 D Stanley 已经提到的。另一种是定义一个具有两个属性的类,如下所示:

public class PersonCity
{
    public string LastName { get; set; }
    public string City { get; set; }
}

然后将查询的每个元素投影到 PersonCity 对象。

lstCurrent context.Customers()
                  .Where(t =>t.type=="current")
                  .Select(c => new PersonCity
                  { 
                      LastName = c.LastName, 
                      City = c.City
                  });

这样做,您现在可以定义lstCurrent 如下:

var lstCurrent = Enumerable.Empty<PersonCity>();

重要提示

如果您的查询返回不同的类型,以上是没有意义的。两个查询(一个在 if 中,另一个在 else 中)应该返回相同的类型。

【讨论】:

    【解决方案4】:

    当期望使用隐式类型声明或重构已经具有隐式类型的代码 (var) 时,这是一个常见的陷阱current accepted answer 非常有效,但将所有表达式变体减少为单个 1-liner linq 表达式很容易影响代码的可读性。

    在这种情况下,由于在查询结束时投影到匿名类型而使问题变得复杂,这可以通过对投影使用显式类型定义来解决,但是将查询构造分解为多个更简单步骤:

    var customerQuery = _context.Customers().AsQueryable();
    if (Type == 1)
        customerQuery = customerQuery.Where(t => t.type == "current");
    else
        customerQuery = customerQuery.Where(...);
    ... // any other filtering or sorting expressions?
    
    var lstCurrent = customQuery.Select(c => new { c.LastName, c.City});
    return View(lstCurrent.ToList());
    

    当然,最后一段可能是单行,或者如果没有进一步引用lstCurrent,编译器可能会将其优化为以下内容:

    return View(customQuery.Select(c => new { c.LastName, c.City}).ToList());
    

    在此示例中,我特意转换为 IQueryable&lt;T&gt;,以确保此解决方案与 IQueryable&lt;T&gt;/DbSet&lt;T&gt; 上下文和存储库样式 IEnumerable&lt;T&gt; 上下文兼容。


    这种变化通常是最先想到的,但是我们仍然在两个地方声明了查询的来源,这增加了这段代码的歧义以及在以后的重构中出现分歧的风险(不小心只编辑了一个分支并且不维护另一个分支中的代码):

    IQueryable<Customer> customerQuery = null;
    if (Type == 1)
        customerQuery = _context.Customers().Where(t => t.type == "current");
    else
        customerQuery = _context.Customers().Where(...);
    ... // any other filtering or sorting expressions?
    var lstCurrent = customQuery.Select(c => new { c.LastName, c.City});
    return View(lstCurrent.ToList());
    

    另一种解决方案是将输出明确定义为其自己的具体类:

    public class CustomerSummary
    {
        public string LastName { get;set; }
        public string City { get;set; }
    }
    ...
    
    List<Customers> customers = null;
    if (Type == 1)
        customers = _context.Customers().Where(c => c.type == "current")
                                        .Select(c => new CustomerSummary
                                        {
                                            LastName = c.LastName,
                                            City = c.City
                                        }).ToList();
    else
        customers = _context.Customers().Where(c => ...)
                                        .Select(c => new CustomerSummary
                                        {
                                            LastName = c.LastName,
                                            City = c.City
                                        }).ToList();
    ... // any other filtering or sorting expressions?
    return View(customers);
    

    一次性的代码很多,但如果你让它足够抽象,它可以在其他场景中重复使用,我仍然会将它与第一个代码示例结合起来,它保留了源、过滤器和投影逻辑分离,在应用程序的整个生命周期中,这 3 个元素往往会发生不同的演变,因此分离出代码使得重构或未来维护更容易完成和审查。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-14
      • 2020-11-10
      • 2010-10-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多