英文:
Is it possible for an extension method to be inlined in C#?
问题
我有一个简单而常见的`string.IsNullOrEmpty`的扩展方法:
```cs
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
public class Program
{
public static void Main()
{
var str = "string!";
Console.WriteLine(string.IsNullOrEmpty(str));
Console.WriteLine(str.IsNullOrEmpty());
}
}
public static class StringExtensions
{
public static bool IsNullOrEmpty([NotNullWhen(false)] this string value)
{
return string.IsNullOrEmpty(value);
}
}
根据ReSharper,在Release配置(Any CPU)中这些方法的IL代码如下:
main:
.method public hidebysig static void
Main() cil managed
{
.entrypoint
.maxstack 8
// [8 9 - 8 29]
IL_0000: ldstr "string!"
// [10 9 - 10 54]
IL_0005: dup
IL_0006: call bool [System.Runtime]System.String::IsNullOrEmpty(string)
IL_000b: call void [System.Console]System.Console::WriteLine(bool)
// [12 9 - 12 48]
IL_0010: call bool StringExtensions::IsNullOrEmpty(string)
IL_0015: call void [System.Console]System.Console::WriteLine(bool)
// [13 5 - 13 6]
IL_001a: ret
} // end of method Program::Main
IsNullOrEmpty:
.method public hidebysig static bool
IsNullOrEmpty(
string 'value'
) cil managed
{
.custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(unsigned int8)
= (01 00 02 00 00 ) // .....
// unsigned int8(2) // 0x02
.custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionAttribute::.ctor()
= (01 00 00 00 )
.param [1]
.custom instance void [System.Runtime]System.Diagnostics.CodeAnalysis.NotNullWhenAttribute::.ctor(bool)
= (01 00 00 00 00 ) // .....
// bool(false)
.maxstack 8
// [20 9 - 20 44]
IL_0000: ldarg.0 // 'value'
IL_0001: call bool [System.Runtime]System.String::IsNullOrEmpty(string)
IL_0006: ret
} // end of method StringExtensions::IsNullOrEmpty
英文:
I have a simple, fairly common extension method for string.IsNullOrEmpty
:
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
public class Program
{
public static void Main()
{
var str = "string!";
Console.WriteLine(string.IsNullOrEmpty(str));
Console.WriteLine(str.IsNullOrEmpty());
}
}
public static class StringExtensions
{
public static bool IsNullOrEmpty([NotNullWhen(false)] this string value)
{
return string.IsNullOrEmpty(value);
}
}
According to ReSharper, the IL in Release config (Any CPU) for these methods is:
main:
.method public hidebysig static void
Main() cil managed
{
.entrypoint
.maxstack 8
// [8 9 - 8 29]
IL_0000: ldstr "string!"
// [10 9 - 10 54]
IL_0005: dup
IL_0006: call bool [System.Runtime]System.String::IsNullOrEmpty(string)
IL_000b: call void [System.Console]System.Console::WriteLine(bool)
// [12 9 - 12 48]
IL_0010: call bool StringExtensions::IsNullOrEmpty(string)
IL_0015: call void [System.Console]System.Console::WriteLine(bool)
// [13 5 - 13 6]
IL_001a: ret
} // end of method Program::Main
IsNullOrEmpty:
.method public hidebysig static bool
IsNullOrEmpty(
string 'value'
) cil managed
{
.custom instance void System.Runtime.CompilerServices.NullableContextAttribute::.ctor(unsigned int8)
= (01 00 02 00 00 ) // .....
// unsigned int8(2) // 0x02
.custom instance void [System.Runtime]System.Runtime.CompilerServices.ExtensionAttribute::.ctor()
= (01 00 00 00 )
.param [1]
.custom instance void [System.Runtime]System.Diagnostics.CodeAnalysis.NotNullWhenAttribute::.ctor(bool)
= (01 00 00 00 00 ) // .....
// bool(false)
.maxstack 8
// [20 9 - 20 44]
IL_0000: ldarg.0 // 'value'
IL_0001: call bool [System.Runtime]System.String::IsNullOrEmpty(string)
IL_0006: ret
} // end of method StringExtensions::IsNullOrEmpty
Adding [MethodImpl(MethodImplOptions.AggressiveInlining)]
or any other seemingly appropriate MethodImplOptions
has no effect. Removing [NotNullWhen(false)]
has no effect.
This method is simple enough that I'd have assumed the compiler would just replace the call on bool StringExtensions::IsNullOrEmpty(string)
with the bool [System.Runtime]System.String::IsNullOrEmpty(string)
.
Is there nothing to be gained by replacing it? Sure, it's just adding a before the call to string.IsNullOrEmpty
, but it's a relatively trivial substitution, right?
答案1
得分: 2
扩展方法只是“语法糖”。从JIT编译器的角度来看,扩展方法调用与其他方法调用没有区别。但是内联是由JIT编译器执行的,因此您需要检查JIT编译器生成的实际汇编代码,而不是编译器生成的IL代码。
可能被内联或不被内联的规则也可能在不同的JIT编译器版本之间有所不同,因此您可能需要检查您感兴趣的每个JIT编译器版本的输出。
英文:
Extension methods are just "syntactic sugar".
From the jitters point of view there is no difference between an extension method call and any other method call. But the inlining will be done by the jitter, so you need to inspect the actual assembly code from the jitter, not the IL code produced by the compiler.
The rules for what may be inlined or not may also vary between jitter versions, so you may need to inspect the output for each jitter version you are interested in.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论