提问人:isakgo_ 提问时间:8/12/2022 最后编辑:isakgo_ 更新时间:8/12/2022 访问量:47
编译器如何在内部转换包含闭包的闭包?
How does the compiler internally translate a closure that contains a closure?
问:
对代码进行了部分修改和添加,以便您可以练习 EFFECTIVE C# (COVERS 6.0) 一书中的内容。
我有一个包含 cloure 的封口。为了理解这段代码,我尝试了编译器如何在内部翻译它。但是,它最终未能绑定到委托。c.importantStatistic
LeakingClosure
是书中的代码,也是我另外编写的编译代码。ResourceHogFilter
CheapNumberGenerator
LeakingClosure
private static IEnumerable<int> LeakingClosure(int mod)
{
var filter = new ResourceHogFilter();
var source = new CheapNumberGenerator();
var results = new CheapNumberGenerator();
var importantStatistic =
(from num in source.GetNumbers(50)
where filter.PassesFilter(num)
select num).Average();
return
from num in results.GetNumbers(100)
where num > importantStatistic
select num;
}
public class ResourceHogFilter
{
public bool PassesFilter(int num)
{
return num < 30;
}
}
public class CheapNumberGenerator
{
public IEnumerable<int> GetNumbers(int size)
{
for (var i = 0; i < size; i++)
yield return i++;
}
}
首先,将查询表达式更改为方法调用语法。
private static IEnumerable<int> METHOD_LeakingClosure(int mod)
{
var filter = new ResourceHogFilter();
var source = new CheapNumberGenerator();
var results = new CheapNumberGenerator();
var importantStatistic = source
.GetNumbers(50)
.Where(num => filter.PassesFilter(num))
.Average();
return results
.GetNumbers(100)
.Where(num => num > importantStatistic);
}
然后,我尝试了以下操作,使其看起来类似于编译器将要翻译的内容:
private static IEnumerable<int> COMPILER_LeakingClosure(int mod)
{
var source = new CheapNumberGenerator();
var results = new CheapNumberGenerator();
var c = new Closure();
c.filter = new ResourceHogFilter();
var importantStatistic = source
.GetNumbers(50)
.Where(new Func<int, bool>(c.filter.PassesFilter))
.Average();
c.importantStatistic = importantStatistic;
return results
.GetNumbers(100)
.Where(new Func<int, bool>(c.importantstatistic)); // compile-time error
}
private sealed class Closure
{
public ResourceHogFilter filter;
public double importantStatistic;
}
但是,它最终未能绑定。因为,返回一个 ,返回一个 .c.importantStatistic
Average()
double
results.GetNumbers()
IEnumerable<int>
编译器正确地表示了类和类的哪些代码?COMPILER_LeakingClosure
Closure
答:
1赞
isakgo_
8/12/2022
#1
当编译器在内部创建嵌套类时,它包括局部变量以及使用局部变量的 lambda 表达式。
private static IEnumerable<int> COMPILER_LeakingClosure(int mod)
{
var source = new CheapNumberGenerator();
var results = new CheapNumberGenerator();
var c = new Closure();
c.filter = new ResourceHogFilter();
var importantStatistic = source
.GetNumbers(50)
.Where(new Func<int, bool>(c.PassesFilter))
.Average();
c.importantStatistic = importantStatistic;
return results
.GetNumbers(100)
.Where(new Func<int, bool>(c.GreaterThanImportantStatistic));
}
private sealed class Closure
{
public ResourceHogFilter filter;
public bool PassesFilter(int num) => filter.PassesFilter(num);
public double importantStatistic;
public bool GreaterThanImportantStatistic(int num) => num > importantStatistic;
}
1赞
Sweeper
8/12/2022
#2
您可以使用 SharpLab.io 来查看编译器将 lambda 降低到什么程度。通常,降低的代码将包含一堆带有不寻常字符的名称,并且很难阅读,但这并非不可能。
LeakingClosure
翻译为:
public static IEnumerable<int> LeakingClosure(int mod)
{
<>c__DisplayClass0_0 <>c__DisplayClass0_ = new <>c__DisplayClass0_0();
<>c__DisplayClass0_.filter = new ResourceHogFilter();
CheapNumberGenerator cheapNumberGenerator = new CheapNumberGenerator();
CheapNumberGenerator cheapNumberGenerator2 = new CheapNumberGenerator();
<>c__DisplayClass0_.importantStatistic = Enumerable.Average(Enumerable.Where(cheapNumberGenerator.GetNumbers(50), new Func<int, bool>(<>c__DisplayClass0_.<LeakingClosure>b__0)));
return Enumerable.Where(cheapNumberGenerator2.GetNumbers(100), new Func<int, bool>(<>c__DisplayClass0_.<LeakingClosure>b__1));
}
<>c__DisplayClass0_0
这里类似于您的类 - 两者都用于捕获 和 。这里的区别在于,它还有两个方法,包含每个 lambda 的主体:Closure
filter
importantStatistic
[CompilerGenerated]
private sealed class <>c__DisplayClass0_0
{
public ResourceHogFilter filter;
public double importantStatistic;
internal bool <LeakingClosure>b__0(int num)
{
return filter.PassesFilter(num);
}
internal bool <LeakingClosure>b__1(int num)
{
return (double)num > importantStatistic;
}
}
使名称更具可读性并改进格式,我们得到以下输出:
private static IEnumerable<int> COMPILER_LeakingClosure(int mod)
{
Closure c = new Closure();
c.filter = new ResourceHogFilter();
CheapNumberGenerator cheapNumberGenerator = new CheapNumberGenerator();
CheapNumberGenerator cheapNumberGenerator2 = new CheapNumberGenerator();
c.importantStatistic = Enumerable.Average(
Enumerable.Where(
cheapNumberGenerator.GetNumbers(50), new Func<int, bool>(c.WhereLambda1)
)
);
return Enumerable.Where(
cheapNumberGenerator2.GetNumbers(100), new Func<int, bool>(c.WhereLambda2)
);
}
private sealed class Closure
{
public ResourceHogFilter filter;
public double importantStatistic;
internal bool WhereLambda1(int num)
{
return filter.PassesFilter(num);
}
internal bool WhereLambda2(int num)
{
return num > importantStatistic;
}
}
评论
0赞
isakgo_
8/12/2022
使用 SharpLab.io,您可以尝试各种事情:D
评论