Dan*_*plo 26 c# function c#-7.0
在C#7中使用本地函数时,如果要将主方法中的参数(或其他局部变量)传递给本地函数,则有两个选项:您可以像任何其他函数一样显式声明参数,也可以简单地从包含方法"捕获"参数/变量并直接使用它们.
一个例子可能说明了这一点:
明确声明
public int MultiplyFoo(int id)
{
return LocalBar(id);
int LocalBar(int number)
{
return number * 2;
}
}
Run Code Online (Sandbox Code Playgroud)
捕获
public int MultiplyFoo(int id)
{
return LocalBar();
int LocalBar()
{
return id * 2;
}
}
Run Code Online (Sandbox Code Playgroud)
两种方法的工作方式相同,但它们调用本地函数的方式不同.
所以我的问题是:
我应该注意两者之间有什么区别吗?我在考虑性能,内存分配,垃圾收集,可维护性等方面.
Jon*_*eet 17
C#中的本地函数在捕获方面非常聪明 - 至少在Roslyn实现中是这样.当编译器能够保证您不是从本地函数创建委托(或者做其他会延长变量生命周期的东西)时,它可以使用一个ref
参数,将生成的结构中的所有捕获变量与之通信本地功能.例如,你的第二种方法最终会像:
public int MultiplyFoo(int id)
{
__MultiplyFoo__Variables variables = new __MultiplyFoo__Variables();
variables.id = id;
return __Generated__LocalBar(ref variables);
}
private struct __MultiplyFoo__Variables
{
public int id;
}
private int __Generated__LocalBar(ref __MultiplyFoo__Variables variables)
{
return variables.id * 2;
}
Run Code Online (Sandbox Code Playgroud)
所以不需要堆分配,因为(例如)将lambda表达式转换为委托.另一方面,有结构的构造,然后将值复制到其中.传递一个int
by值是否比通过引用传递struct更有效或更低效率不太可能有意义......虽然我猜想在你有一个巨大的struct作为局部变量的情况下,这意味着使用隐式捕获会更多比使用简单的值参数更有效.(同样,如果您的本地函数使用了大量捕获的局部变量.)
当你有多个局部变量被不同的局部函数捕获时,情况已经变得更加复杂 - 当其中一些局部函数是循环内的局部函数时更是如此.使用ildasm
或反射器等进行探索可能非常有趣.
一旦你开始做任何复杂的事情,比如编写异步方法,迭代器块,本地函数中的lambda表达式,使用方法组转换从本地函数等创建委托......那时我会犹豫继续猜测.您可以尝试以各种方式对代码进行基准测试,或者查看IL,或者只是编写更简单的代码并依赖于更大的性能验证测试(您已经拥有,对吧?:)来告诉您是否存在问题.
这是一个有趣的问题.首先,我已经反编译了构建输出.
public int MultiplyFoo(int id)
{
return LocalFunctionTests.\u003CMultiplyFoo\u003Eg__LocalBar\u007C0_0(id);
}
public int MultiplyBar(int id)
{
LocalFunctionTests.\u003C\u003Ec__DisplayClass1_0 cDisplayClass10;
cDisplayClass10.id = id;
return LocalFunctionTests.\u003CMultiplyBar\u003Eg__LocalBar\u007C1_0(ref cDisplayClass10);
}
Run Code Online (Sandbox Code Playgroud)
当您将id作为参数传递时,将使用传递的id参数调用本地函数.没有什么花哨的,参数存储在方法的堆栈框架上.但是,如果不传递参数,则会使用字段(cDisplayClass10.id = id)创建一个结构(以Daisy指出的名称为"class"),并为其分配id.然后将结构作为引用传递给本地函数.C#编译器似乎是为了支持闭包.
在性能方面,我使用了Stopwatch.ElapsedTicks,传递id作为参数始终更快.我认为这是因为使用字段创建结构的成本.当我向本地函数添加另一个参数时,性能差距扩大了.
这是我的测试代码,如果有人感兴趣的话
public int MultiplyFoo(int id, int id2)
{
return LocalBar(id, id2);
int LocalBar(int number, int number2)
{
return number * number2 * 2;
}
}
public int MultiplyBar(int id, int id2)
{
return LocalBar();
int LocalBar()
{
return id * id2 * 2;
}
}
[Fact]
public void By_Passing_Id()
{
var sut = new LocalFunctions();
var watch = Stopwatch.StartNew();
for (int i = 0; i < 10000; i++)
{
sut.MultiplyFoo(i, i);
}
_output.WriteLine($"Elapsed: {watch.ElapsedTicks}");
}
[Fact]
public void By_Not_Passing_Id()
{
var sut = new LocalFunctions();
var watch = Stopwatch.StartNew();
for (int i = 0; i < 10000; i++)
{
sut.MultiplyBar(i, i);
}
_output.WriteLine($"Elapsed: {watch.ElapsedTicks}");
}
Run Code Online (Sandbox Code Playgroud)