在C#中,扩展方法是否可以进行内联优化?

huangapple go评论59阅读模式
英文:

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.

huangapple
  • 本文由 发表于 2023年3月7日 21:42:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/75662738.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定