【问题标题】:Concatenation of IEnumerable collections C#连接 IEnumerable 集合 C#
【发布时间】:2015-04-04 16:03:44
【问题描述】:

我有 2 个 Linq 查询结果,我想做一些字符串操作和连接。

结果 1 是 group1 中启用的复选框的名称,由

获得
var selectedCarPosts = grpBox1MCar.Controls.OfType<CheckBox>()
.Where(c => c.Checked).OrderBy(c => c.Name).Select(c => c.Name);

产生结果 1:

NearMainGate
NearMP5WestGate

结果2是group2中启用的复选框的名称,由

var selectedDtTypeCars = gbDataTypeMCars.Controls.OfType<CheckBox>()
.Where(c => c.Checked).OrderBy(c => c.Name).Select(c => c.Name);

产生 Result2:

WindDir
WindVel

希望从这两个结果中得到一个串联列表,如下所示:(Result3)

C.NearMainGate
C.NearMP5WestGate
C.WindDirNearMainGate
C.WindDirNearMP5WestGate
C.WindVelNearMainGate
C.WindVelNearMP5WestGate

这些在以后的动态 sql 查询中形成列。

我有下面的代码来一步一步完成这个:

var s1 = selectedCarPosts.Select(s => "C." + s); //the first 2 items in Result3
//Now to get the rest, by inserting Result2 string in each of the first 2 items of Result3
IEnumerable<string> selCarPostsArrWithC = new string[]{};
IEnumerable<string> s2 = new string[]{};
foreach (var type in selectedDtTypeCars)
{
   selCarPostsArrWithC = s1.Select(s => s.Insert(2, type));//C.WindDirNearMainGate C.WindDirNearMP5WestGate in FIRST iteration and so on
   s2 = s2.Concat(selCarPostsArrWithC);// as soon as the SECOND iteration starts, the previous s2 list is overwritten with the subsequent result in selCarPostsArrWithC 
 }

这里的问题是,在代码调试过程中,我注意到,当我在 foreach 行之后点击 F10 键时,在实际到达 foreach 块之前,s2 中的先前值是已经被 selCarPostsArrWithC 中的后续结果覆盖。下面解释

对于第一次迭代,s2 有结果。

[0] "C.WindDirNearMainGate"
[1] "C.WindDirNearMP5WestGate"

在进入foreach块之前的第二次迭代开始

s2 已经通过 WindVel 重置为新值:

[0] "C.WindVelNearMainGate"
[1] "C.WindVelNearMP5WestGate"

请任何人帮助我做错了什么?对于 IEnumerable 列表,如何以粗体完成 Result3?

【问题讨论】:

    标签: c# linq


    【解决方案1】:

    Enumerable.Select 在您调用它时不会做任何重要的事情,它只是进行设置,以便稍后根据需要执行请求的工作。

    所以当你写的时候

    selCarPostsArrWithC = s1.Select(s => s.Insert(2, type));
    

    这还没有调用string.Insertstring.Insert 仅在您(或您的调试器)稍后开始迭代 selCarPostsArrWithC 时调用。

    通常,这无关紧要,除非您多次迭代可枚举对象时的性能。但是,在这里,因为 string.Insert 的调用时间比您预期的要晚,所以您传递给它的参数的评估时间也比您预期的要晚。您只有一个 type 变量,并且该变量在被读取时已经保存了下一个值。

    通常,您可以通过每次迭代创建一个新变量来解决此问题,该变量捕获该迭代期间看到的 type 的值:

    foreach (var type in selectedDtTypeCars)
    {
      var type_ = type;
      selCarPostsArrWithC = s1.Select(s => s.Insert(2, type_));
      s2 = s2.Concat(selCarPostsArrWithC);
    }
    

    (如今,C# 已经在后台为 foreach 执行此操作,但如果使用较旧的编译器,您可能需要像这样写出来。)

    或者,或者,直接在循环体内执行所有评估:

    foreach (var type in selectedDtTypeCars)
    {
      selCarPostsArrWithC = s1.Select(s => s.Insert(2, type)).ToList();
      s2 = s2.Concat(selCarPostsArrWithC);
    }
    

    虽然在这种情况下,最好将s2 设为List&lt;string&gt;,并调用其AddRange 方法。

    【讨论】:

    • 嗨hvd。感谢您的信息..我尝试了 Cameron 的解决方案.. 它奏效了。但还是谢谢。您的评论回答了为什么我的代码不起作用。很多 LINQ 的东西要学 :-)。
    【解决方案2】:

    基本上你有一个前缀列表和一个后缀列表。 (注意:我浏览了您的代码,但找不到您在哪里获得附加到每个值的 C.。)

    对于每个前缀,您都需要一个后缀。

    这是SelectMany()的用法

    我简化了您的代码,因为您提供了相当多的代码。但这是我想出的:

        var location = new[]
        {
          "NearMainGate", 
          "NearMP5WestGate"
        };
    
        var modifier = new[]
        {
          "WindDir", 
          "WindVel"
        };
    
        var lambdaResult = modifier.SelectMany(s => location.Select(l => string.Format("{0}{1}{2}", "C.", s, l)));
    
        var queryResult =
            from m in modifier
            from l in location
            select string.Format("{0}{1}{2}", "C.", m, l);
    

    请注意,有两种解决方案:linq 查询语法和 linq lambda 语法。

    在这种情况下,我认为查询语法更简洁,更易于阅读,但对每个人来说都是自己的。

    这里有一个功能证明:https://dotnetfiddle.net/Z61RsI

    【讨论】:

    • 嗨卡梅伦!哇,太棒了。非常感谢。(点赞需要 15 分。现在只有 7 分。一旦我达到 15 分,我就会这样做)现在我意识到我对 LINQ 有多么陌生 .. 除了掌握 C# 之外,还需要学习更多 LINQ 语法. (你可能已经明白我是一只新蜜蜂);-) 你的 lambda 和查询结果都像一个魅力。你能推荐一些用 C# 进行 LINQ 和 Winforms 编程的好书、资源吗?抱歉跑题了。
    • @Nish 有 资源。我个人从查看工作代码中学到的东西最好。 MSDN's Linq Resources 很棒。如果您想要代码示例,Microsoft 有 101 LInq Samples 可以查看。
    • 谢谢卡梅伦。说得通。是的!祝你有美好的一天
    猜你喜欢
    • 1970-01-01
    • 2017-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-09
    • 2018-08-22
    • 1970-01-01
    • 2013-11-12
    相关资源
    最近更新 更多