292

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

注意:答案几乎完全涉及内联函数的能力,即“用被调用者的主体替换函数调用站点的手动或编译器优化”。如果您对匿名(又名 lambda)函数感兴趣,请参阅@jalf 的回答每个人都在谈论的这个“Lambda”是什么?.

4

14 回答 14

417

最后在 .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. Previously "force" was used here. I'll try to clarify the term. As in the comments and the documentation, The method should be inlined if possible. Especially considering Mono (which is open), there are some mono-specific technical limitations considering inlining or more general one (like virtual functions). Overall, yes, this is a hint to compiler, but I guess that is what was asked for.

于 2012-01-05T16:38:01.093 回答
90

内联方法只是一种编译器优化,其中函数的代码被滚动到调用者中。

在 C# 中没有执行此操作的机制,并且它们将在支持它们的语言中谨慎使用——如果您不知道为什么应该在某处使用它们,那么它们不应该使用。

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

  1. 在不需要的情况下使用内联很容易制作大量二进制文件
  2. 从性能的角度来看,当某些东西应该被内联时,编译器往往比你更清楚

最好不要管它,让编译器完成它的工作,然后分析并确定 inline 是否是最适合您的解决方案。当然,有些事情只是内联才有意义(尤其是数学运算符),但让编译器处理它通常是最佳实践。

于 2009-01-23T17:33:06.527 回答
61

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

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

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

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

于 2010-01-16T22:48:59.897 回答
34

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

另一方面,lambda 函数纯粹是一个语义概念。对如何实现或执行它们没有要求,只要它们遵循语言规范中规定的行为即可。如果 JIT 编译器喜欢,它们可以被内联,如果不喜欢,它们可以被内联。

C# 中没有 inline 关键字,因为它是一种通常可以留给编译器的优化,尤其是在 JIT ed 语言中。JIT 编译器可以访问运行时统计信息,这使其能够比编写代码时更有效地决定内联的内容。如果编译器决定内联一个函数,那么无论哪种方式你都无能为力。:)

于 2009-01-23T17:40:05.187 回答
23

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<string> BuildListAndOutput(IEnumerable<string> x)
{  // let's pretend IEnumerable<T>.ToList() doesn't exist for the moment
    IList<string> result = new List<string>();

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

编译器即时优化器可以选择更改代码以避免在堆栈上重复调用 OutputItem(),这样就好像您已经编写了这样的代码:

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

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

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

    return result;
}

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

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

于 2009-01-23T17:38:58.543 回答
21

您是指 C++ 意义上的内联函数吗?其中一个普通函数的内容会自动内联复制到调用站点中?最终效果是调用函数时实际上没有发生函数调用。

例子:

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

如果是这样,那么没有,没有与此等效的 C#。

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

例子:

static void Example() {
  Func<int,int,int> add = (x,y) => x + y;
  var result = add(4,6);  // 10
}
于 2009-01-23T17:38:36.813 回答
7

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

简化(不使用表达式):

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

所以一个Action<T>代表就足够了..说:

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

和说的一样:

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

不同之处在于参数类型和委托声明是通过使用来推断的,而简单的内联方法不需要大括号。

然而

List<T>.Where接受一个函数,期待一个结果。

因此,Function<T, bool>可以预期:

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

这与以下内容相同:

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

您还可以内联声明这些方法并将它们分配给变量 IE:

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

myAction();

或者

Function<object, string> myFunction = theObject => theObject.ToString();

string myString = myFunction(someObject);

我希望这有帮助。

于 2009-01-23T17:38:52.397 回答
2

在某些情况下,我确实希望强制内联代码。

例如,如果我有一个复杂的例程,其中在一个高度迭代的块中做出大量决策,并且这些决策导致要执行的操作相似但略有不同。例如,考虑一个复杂的(非 DB 驱动的)排序比较器,其中排序算法根据许多不同的不相关标准对元素进行排序,例如,如果它们根据快速语言的语法和语义标准对单词进行排序,则可能会这样做识别系统。我倾向于编写辅助函数来处理这些操作,以保持源代码的可读性和模块化。

我知道这些辅助函数应该是内联的,因为如果代码永远不需要被人类理解,那就是编写代码的方式。我当然想确保在这种情况下没有函数调用开销。

于 2010-06-29T20:04:56.580 回答
2

声明“最好不要管这些事情,让编译器完成工作......”(Cody Brocious)完全是垃圾。我已经编写高性能游戏代码 20 年了,但我还没有遇到过一个“足够聪明”的编译器来知道哪些代码应该被内联(函数)。在 c# 中使用“内联”语句会很有用,事实是编译器只是没有确定哪个函数应该始终内联或在没有“内联”提示的情况下不应该内联的所有信息。当然如果函数很小(访问器)那么它可能会自动内联,但是如果它是几行代码呢?废话,编译器无法知道,你不能把它留给编译器来优化代码(超出算法)。

于 2011-03-04T22:21:07.293 回答
1

I know this question is about C#. However, you can write inline functions in .NET with F#. see: Use of `inline` in F#

于 2012-12-22T12:45:43.627 回答
0

不,C# 中没有这样的构造,但 .NET JIT 编译器可以决定在 JIT 时间执行内联函数调用。但我其实不知道它是否真的在做这样的优化。
(我认为应该:-))

于 2009-01-23T17:43:14.667 回答
0

如果您的程序集将被 ngen-ed,您可能需要查看 TargetedPachingOptOut。这将帮助 ngen 决定是否内联方法。MSDN 参考

不过,它仍然只是一个优化的声明性提示,而不是命令式命令。

于 2011-11-22T19:42:07.113 回答
-7

Lambda 表达式是内联函数!我认为,C# 没有像 inline 或类似的额外属性!

于 2009-01-23T17:37:22.307 回答
-8

C# 不支持像 python 这样的动态语言那样的内联方法(或函数)。但是,匿名方法和 lambda 可以用于类似的目的,包括当您需要访问包含方法中的变量时,如下例所示。

static void Main(string[] args)
{
    int a = 1;

    Action inline = () => a++;
    inline();
    //here a = 2
}
于 2009-01-23T17:44:33.477 回答