这段代码
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication
{
internal class Program
{
public static void Main()
{
var values = new[] {1, 2, 3, 3, 2, 1, 4};
var distinctValues = GetDistinctValuesUsingWhere(values);
Console.WriteLine("GetDistinctValuesUsingWhere No1: " + string.Join(",", distinctValues));
Console.WriteLine("GetDistinctValuesUsingWhere No2: " + string.Join(",", distinctValues));
distinctValues = GetDistinctValuesUsingForEach(values);
Console.WriteLine("GetDistinctValuesUsingForEach No1: " + string.Join(",", distinctValues));
Console.WriteLine("GetDistinctValuesUsingForEach No2: " + string.Join(",", distinctValues));
Console.ReadLine();
}
private static IEnumerable<T> GetDistinctValuesUsingWhere<T>(IEnumerable<T> items)
{
var set=new HashSet<T>();
return items.Where(i=> set.Add(i));
}
private static IEnumerable<T> GetDistinctValuesUsingForEach<T>(IEnumerable<T> items)
{
var set=new HashSet<T>();
foreach (var i in items)
{
if (set.Add(i))
yield return i;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
得到以下输出:
GetDistinctValuesUsingWhere No1:1,2,3,4
GetDistinctValuesUsingWhere No2:
GetDistinctValuesUsingForEach No1:1,2,3,4
GetDistinctValuesUsingForEach No2:1,2,3,4
我不明白为什么我在"GetDistinctValuesUsingWhere No2"行中没有得到任何值.
任何人都可以向我解释这个吗?
在Scott的回答之后更新,我将示例更改为以下内容:
private static IEnumerable<T> GetDistinctValuesUsingWhere2<T>(IEnumerable<T> items)
{
var set = new HashSet<T>();
var capturedVariables = new CapturedVariables<T> {set = set};
foreach (var i in items)
if (capturedVariables.set.Add(i))
yield return i;
//return Where2(items, capturedVariables);
}
private static IEnumerable<T> Where2<T>(IEnumerable<T> source, CapturedVariables<T> variables)
{
foreach (var i in source)
if (variables.set.Add(i))
yield return i;
}
private class CapturedVariables<T>
{
public HashSet<T> set;
}
Run Code Online (Sandbox Code Playgroud)
这将导致输出1,2,3,4的两倍.
但是,如果我只是取消注释该行
return Where2(items,capturedVariables);
并评论这些线条
foreach(var i in items)if(capturedVariables.set.Add(i))yield return i;
在GetDistinctValuesUsingWhere2方法中,我只得到输出1,2,3,4一次.这是删除的行和现在未注释的方法完全相同.
我还是不明白......
GetDistinctValuesUsingWhere No2没有返回任何结果的原因是因为变量捕获.
你的where方法更像是这个函数
private static IEnumerable<T> GetDistinctValuesUsingWhere<T>(IEnumerable<T> items)
{
var set=new HashSet<T>();
var capturedVariables = new CapturedVariables {set = set}
return Where(items, capturedVariables);
}
IEnumerable<T> Where(IEnumerable<T> source, CapturedVariables variables)
{
foreach (var i in items)
{
if (variables.set.Add(i))
yield return i;
}
}
Run Code Online (Sandbox Code Playgroud)
所以这两种方法都yield return
在幕后,但GetDistinctValuesUsingWhere为每次调用重用hashset,其中GetDistinctValuesUsingForEach为每个枚举生成一个新的hashset.
回答更新版本:
GetDistinctValuesUsingWhere2()
包含foreach
循环的方法,返回IEnumerable
捕获了闭包中方法的全部内容,包括set
初始化语句。因此,每次开始迭代可枚举时都会执行此语句,但不会在原始调用期间执行GetDistinctValuesUsingWhere2()
。Where2()
,该GetDistinctValuesUsingWhere2()
方法不需要捕获该方法的内容,因为您没有在其中定义迭代器或委托。相反,您将Where2()
作为IEnumerable
. 后一种方法仅捕获foreach
循环及其参数(已初始化),但不捕获set
初始化语句本身。因此,这一次,set 初始化语句将仅在原始调用期间执行一次GetDistinctValuesUsingWhere2()
。如有必要,请在代码中的各个位置放置一些断点:这应该可以帮助您理解我在这里试图解释的内容。