Linq查询从多个列表中选择单个字符串

本文关键字:选择 单个 字符串 string 查询 列表 Linq | 更新日期: 2023-09-27 18:04:53

我很难理解为什么我得到了这样的结果。

我有两个字符串列表:
var list1 = new List<String> {"item 1", "item 2"};
var list2 = new List<String> { "item 3", "item 4" };

版本1 -输出:"item 2"

var item =
            from x in (list1.Concat(list2))
            where x.EndsWith("2")
            select x;
        Console.WriteLine(item.First());

版本2 -输出:"i"

var item =
            from x in (list1.Concat(list2))
            where x.EndsWith("2")
            select x.First();
        Console.WriteLine(item.First());

版本3 -输出:"System.Linq.Enumerable+WhereEnumerableIterator ' 1[System.String]"

var item =
            from x in (list1.Concat(list2))
            where x.EndsWith("2")
            select x;
        Console.WriteLine(item);

假设版本2输出"i",我希望版本3输出"item 2"。为什么会出现这种行为?

Linq查询从多个列表<string>中选择单个字符串

在版本3中,select x返回符合条件的字符串的序列;它恰好是一个序列,其中只有一个元素。

Console.WriteLine内部调用.ToString()的任何你传递给它。因为IEnumerable<T>没有有意义的字符串表示,所以. net中的默认是打印类型的字符串名称。

根据你的措辞,我认为你的部分困惑确实来自于对为什么版本2是这样工作的误解。在版本2中,select x.First()实际上有点奇怪/巧合,因为字符串也是IEnumerable<char>,所以您可以对字符串执行LINQ操作。对于每个符合条件的结果,.First()返回该char序列的第一个元素。所以你说:

"对于每个符合我的条件的元素,选择第一个字符,然后返回匹配的所有第一个字符的序列。"

所以实际上,版本2中的item是包含一个元素的IEnumerable<char>。在IEnumerable<char>上调用Console.WriteLine()只会按顺序打印字符。所以你得到"i"

(注意:在我回答这个问题之后,我看到这个问题被编辑为在投影和结果中调用.First(),所以关于将IEnumerable<char>传递给Console.WriteLine的位不再完全相关)

请记住,LINQ基本上是在处理集合,直到你显式地减少它们。例如,Select只是一个投影或变换。它返回传递给它的经过转换的相同数量的项。Where减少了集合,但它仍然是一个集合。

你的版本2是从字符串x.First()选择第一个项目/字符,而你的第一个版本是从结果集->第一个字符串选择第一个项目。

版本1类似于- 从结果集中选择第一项

var item =  (from x in (list1.Concat(list2))
            where x.EndsWith("2")
            select x).First(); //First complete string will be selected

和版本2类似于- 从结果集

中的字符串中选择第一项
var item =  from x in (list1.Concat(list2))
            where x.EndsWith("2")
            select x.First(); //only first char will be selected for string

第三种情况是选择一个IEnumerable<string>,所以当你调用Console.WriteLine时,它调用ToString的默认实现,因此你得到

" System.Linq.Enumerable + WhereEnumerableIterator ' 1 system . string] ["

当使用First()时,您正在具体化列表,导致迭代器执行。这是一种急切的执行。第三个版本使用select,它没有实现列表;使用Defeered Execution,它返回一个迭代器,因此调用ToString(),返回迭代器名称

因为Where返回一个IEnumerable .

你写了相当于:

var whoKnows = list1.Concat(list2).Where(x => x.EndsWith("2"));
Console.WriteLine(whoKnows);

集合的ToString只返回类名。

版本2中x的类型为String。版本1中item的类型是IEnumerable

所以你的版本2返回一个字符列表,这些字符是每个字符串的第一个字符。在版本1中,item.First()返回结果集的第一个元素,该元素是一个字符串。

//This raise a exception if no item found
var item=list1.Concat(list2).First(i => i.EndsWith("2"));
//this return default value (null) if no item found
var item2 = list1.Concat(list2).FirstOrDefault(i => i.EndsWith("2"));