当前位置:  开发笔记 > 编程语言 > 正文

C#中的内联函数?

如何解决《C#中的内联函数?》经验,为你挑选了7个好方法。

你如何在C#中做"内联函数"?我认为我不理解这个概念.他们喜欢匿名方法吗?像lambda函数?

注意:答案几乎完全处理内联函数的能力,即"用被调用者的主体替换函数调用站点的手动或编译器优化".如果您对匿名(也称为lambda)函数感兴趣,请参阅@ jalf的答案或者每个人都在说什么'Lambda'?.



1> konrad.krucz..:

最后在.NET 4.5中,CLR允许使用值提示/建议1方法内联MethodImplOptions.AggressiveInlining.它也可以在Mono的后备箱中使用(今天提交).

// The full attribute usage is in mscorlib.dll,
// so should not need to include extra references
using System.Runtime.CompilerServices; 

...

[MethodImpl(MethodImplOptions.AggressiveInlining)]
void MyMethod(...)

1.此前使用的是"force".由于有一些downvotes,我会试着澄清这个词.正如在评论和文档中一样,The method should be inlined if possible.特别是考虑到Mono(开放),考虑内联或更普遍的(如虚拟功能),存在一些特定于单一的技术限制.总的来说,是的,这是对编译器的暗示,但我想这就是要求的.


+1 - 更新了您的答案,以更具体地了解框架版本要求.
相比之下,C++的内联建议,甚至是编译器特定的建议,实际上并不强制内联:并非所有函数都可以内联(从根本上讲,递归函数很难,但也有其他情况).所以是的,这种"不太"的力量是典型的.
可以与所有.NET版本一起使用的另一种方法是将稍微过大的方法拆分为两个方法,一个调用另一个方法,两个方法都不超过32个字节的IL.净效果就好像原始内联.
它仍然可能不是*力*内联,但在大多数情况下,重写JITters启发式肯定是足够的.
它并没有强制"内联"它只是尝试与JIT交谈,并告诉它程序员真的想在这里使用Inlining,但是JIT对它有最后的说法.因此MSDN:如果可能,应该内联该方法.
我会说"如果可能的话"会推动它; 有很多情况下,可能会延长抖动以处理,甚至可能有一天,它不会在线.目前似乎只是忽略了规模限制,但由于我们可以预期在未来几年内这方面的变化,或许最好只是说"让它更积极地决定是否内联".

2> Cody Brociou..:

内联方法只是一种编译器优化,其中函数的代码被卷入调用者.

在C#中没有任何机制可以做到这一点,并且它们在支持它们的语言中被谨慎使用 - 如果你不知道为什么它们应该被用在某个地方,它们就不应该被使用.

编辑:为了澄清,有两个主要原因需要谨慎使用:

    在没有必要的情况下,通过内联使用大量二进制文件很容易

    从性能的角度来看,编译器往往比你应该知道更好

最好不要管它,让编译器完成它的工作,然后分析并确定内联是否是最适合你的解决方案.当然,有些事情是有意义的内联(特别是数学运算符),但让编译器处理它通常是最好的做法.


关于编译器最了解的论点是错误的.它不会内联任何大于32个IL字节周期的方法.这对于我们无能为力的探测器确定的热点项目(如我的,以及上面的Egor)来说太可怕了.没什么,除了剪切和粘贴代码,所以手动内联.总之,当你真正推动表演时,这是一种可怕的事态.
通常我认为编译器处理内联是可以的.但有些情况下,我喜欢覆盖编译器决策和内联或不内联方法.
@Poma这是使用内联的一个奇怪的原因.我非常怀疑它会证明是有效的.
@Joel Coehoorn:这将是不好的做法,因为它会破坏模块化和访问保护.(考虑一个访问私有成员的类中的内联方法,并从代码中的不同点调用!)
内联显得更加微妙.内存具有快速访问的高速缓存,并且代码的当前部分就像变量一样存储在那些高速缓存中.加载下一个高速缓存行指令是高速缓存未命中,并且成本可能是单个指令的10倍或更多倍.最终它必须加载到L2缓存中,这更加昂贵.因此,膨胀的代码可能导致更多的高速缓存丢失,其中在整个时间内驻留在相同的L1高速缓存行中的内联函数可以导致更少的高速缓存未命中和可能更快的代码.与.Net相比,它更加复杂.

3> BACON..:

更新:根据konrad.kruczynski的回答,以下版本适用于4.0及以下版本的.NET.

您可以使用MethodImplAttribute类来防止方法被内联...

[MethodImpl(MethodImplOptions.NoInlining)]
void SomeMethod()
{
    // ...
}

...但是没有办法做相反的事情并强迫它被内联.


`GetExecutingAssembly`和`GetCallingAssembly`可以根据方法是否内联而给出不同的结果.强制方法非内联消除了任何不确定性.
如果你收到一个调用堆栈(即NullReferenceException),显然堆栈顶部的方法无法抛出它.当然,其中一个电话可能有.但是哪一个?
有趣的是,知道这一点,但为什么要阻止一种方法被内联?我花了一些时间盯着显示器,但我无法理解为什么内联会造成任何伤害.
希望我能+2因为你的名字值得+1:D.

4> jalf..:

你混合了两个不同的概念.函数内联是一种编译器优化,它对语义没有影响.无论内联与否,函数的行为都相同.

另一方面,lambda函数纯粹是一个语义概念.只要它们遵循语言规范中规定的行为,就不需要如何实现或执行它们.如果JIT编译器感觉如此,则可以内联它们,如果不是,则可以内联它们.

C#中没有内联关键字,因为它是一种优化,通常可以留给编译器,特别是在JIT的语言中.JIT编译器可以访问运行时统计信息,这使得它可以比编写代码时更有效地决定内联内容.如果编译器决定使用函数,那么无论如何都无法对它进行任何操作.:)


"无论是否内联,函数的行为都相同." 在一些罕见的情况下,这是不正确的:即想要了解堆栈跟踪的日志记录功能.但我不想过多地破坏你的基本陈述:它是_generally_ true.

5> JaredPar..:

你的意思是C++意义上的内联函数吗?其中普通函数的内容是否自动内嵌到调用点?最终结果是在调用函数时实际上没有发生函数调用.

例:

inline int Add(int left, int right) { return left + right; }

如果是,那么不,没有C#等同于此.

或者你的意思是在另一个函数中声明的函数?如果是,那么是的,C#通过匿名方法或lambda表达式支持这一点.

例:

static void Example() {
  Func add = (x,y) => x + y;
  var result = add(4,6);  // 10
}



6> Joel Coehoor..:

Cody说得对,但我想提供一个内联函数的例子.

假设你有这个代码:

private void OutputItem(string x)
{
    Console.WriteLine(x);

    //maybe encapsulate additional logic to decide 
    // whether to also write the message to Trace or a log file
}

public IList BuildListAndOutput(IEnumerable x)
{  // let's pretend IEnumerable.ToList() doesn't exist for the moment
    IList result = new List();

    foreach(string y in x)
    {
        result.Add(y);
        OutputItem(y);
    }
    return result;
}

编译器刚刚在优化器可以选择修改密码,以避免重复在堆栈上放置到OutputItem()的调用,因此,这将是,如果你已经写成这样的代码:

public IList BuildListAndOutput(IEnumerable x)
{
    IList result = new List();

    foreach(string y in x)
    {
        result.Add(y);

        // full OutputItem() implementation is placed here
        Console.WriteLine(y);   
    }

    return result;
}

在这种情况下,我们会说OutputItem()函数是内联的.请注意,即使从其他位置调用OutputItem(),它也可能会这样做.

编辑以显示更可能被内联的场景.


只是为了澄清; 它是进行内联的JIT; 不是C#编译器.

7> Quintin Robi..:

是的,唯一的区别是它返回一个值.

简化(不使用表达式):

List.ForEach 采取行动,它不期望返回结果.

所以Action代表就够了..说:

List.ForEach(param => Console.WriteLine(param));

和说:

List.ForEach(delegate(T param) { Console.WriteLine(param); });

区别在于param类型和委托decleration是由用法推断的,并且在简单的内联方法中不需要括号.

在哪里

List.Where 采取功能,期待结果.

所以Function我们期待:

List.Where(param => param.Value == SomeExpectedComparison);

这与:

List.Where(delegate(T param) { return param.Value == SomeExpectedComparison; });

您还可以内联声明这些方法并将它们与变量IE一起对齐:

Action myAction = () => Console.WriteLine("I'm doing something Nifty!");

myAction();

要么

Function myFunction = theObject => theObject.ToString();

string myString = myFunction(someObject);

我希望这有帮助.

推荐阅读
N个小灰流_701
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有