你好,如何在C#中实现"内联函数"?我不确定我理解这个概念。它们像匿名方法吗?就像lambda函数一样吗?
注意:答案几乎完全涉及到内联函数的能力,即“手动或编译器优化,将函数调用点替换为被调用者的主体”。如果你对匿名(又名lambda)函数感兴趣,请参见@jalf的答案或这是什么“Lambda”?。
你好,如何在C#中实现"内联函数"?我不确定我理解这个概念。它们像匿名方法吗?就像lambda函数一样吗?
注意:答案几乎完全涉及到内联函数的能力,即“手动或编译器优化,将函数调用点替换为被调用者的主体”。如果你对匿名(又名lambda)函数感兴趣,请参见@jalf的答案或这是什么“Lambda”?。
在 .NET 4.5 中,CLR 允许使用 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”这个词。我会尝试澄清这个术语。在注释和文档中,如果可能应该内联方法。
特别是考虑到 Mono(它是开源的),有一些针对内联或更一般问题(如虚函数)的特定技术限制。总的来说,是的,这是给编译器的一个提示,但我想那就是被要求的。
内联方法是编译器的一种优化方式,其中函数的代码被嵌入到调用者中。
C#中没有执行此操作的机制,在支持它们的语言中应谨慎使用--如果您不知道何时应该在某个位置使用它们,则不应该使用。
编辑:为了澄清,需要谨慎使用它们有两个主要原因:
最好让事情保持原样,让编译器完成其工作,然后进行性能分析并确定内联是否是最佳方案。当然,某些事情只是理所当然地进行内联(特别是数学运算符),但是让编译器处理通常是最佳实践。
更新:根据konrad.kruczynski的答案,以下内容适用于.NET版本包括4.0及以前版本。
您可以使用MethodImplAttribute类来防止一个方法被内联编译...
[MethodImpl(MethodImplOptions.NoInlining)]
void SomeMethod()
{
// ...
}
...但是没有办法相反地强制使其成为内联。
GetExecutingAssembly
和GetCallingAssembly
可能会给出不同的结果。强制一个方法为非内联可以消除任何不确定性。 - stusmith[MethodImpl(MethodImplOptions.AggressiveInlining)]
,但是虽然 NoInlining
被决定性地执行,但后者仅向 JIT 提供建议。此外,除了 @stusmith 已经提到的语义差异之外,我还会添加另一个语义差异,如下所示... - Glenn Slaydenin
,ref
或out
(托管指针)参数的函数,如果不固定,就需要负责确保每个参数在垃圾回收中可见。对于非内联方法,所需的堆栈或使用rcx``rdx
寄存器(垃圾回收也知道)是传递参数时的额外副产品。然而,在x64 JIT中,这种“自由”的可见性并不是保证的。当进行内联处理时,内联合格方法的托管指针参数可能完全从堆栈中优化出去,因此不在垃圾回收的可见性范围内。唯一确保GC正确性的方法是将该方法标记为“NoInlining”。 - Glenn Slayden你把两个不同的概念混淆了。函数内联是编译器优化,对语义没有影响。无论函数是否被内联,它的行为都是相同的。
另一方面,lambda函数纯粹是一个语义概念。没有要求如何实现或执行它们,只要它们遵循语言规范中设定的行为。如果JIT编译器认为合适,它们可以被内联,否则就不会被内联。
C# 中没有内联关键字,因为这是一种通常可以留给编译器优化的技术,特别是在JIT编译的语言中。JIT编译器有访问运行时统计信息的权限,使其能够更有效地决定内联什么,而当你编写代码时,你不能做出任何改变。 :)
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;
}
< p > Just-In-Time优化器可以选择修改代码,以避免重复将调用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;
}
inline int Add(int left, int right) { return left + right; }
如果是这样的话,那么没有C#对应的功能。
或者你是指在另一个函数内声明函数吗?如果是的话,那么通过匿名方法或lambda表达式,C#可以支持这种操作。
例如:
static void Example() {
Func<int,int,int> add = (x,y) => x + y;
var result = add(4,6); // 10
}
没错,唯一的区别就是它返回一个值。
简化(不使用表达式):
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);
我知道这个问题是关于C#的。然而,在.NET中,你可以使用F#编写内联函数。请参见:F#中使用`inline`