静态函数是否等效于C#中的静态Func成员?

Dav*_*d J 10 c# methods lambda static static-methods

看起来静态方法静态Func字段相同.我错过了什么,或者它们本质上是可以互换的(相同的足迹等)?

静态属性最终与另外两个示例相同,除了它包括"get"访问器的(最小)开销.

也许有点毫无意义,毫无顾忌地要问......但我喜欢理解"幕后"发生了什么,即使它并不是立即相关的.

可以肯定的是:我不打算将所有静态方法切换为lambda表达式(并让我的同事疯狂).但是,可能存在一些合理的情况,其中静态变量比编写方法更有意义.或许相反:说服某人使用静态方法而不是lambda表达式来使代码更具可读性或其他

另外,我很好奇是否有更好的方法来调查这些问题

我的考试

我将这个简单的例子放入LINQPad(v4.57.02,"使用/ optimize +编译"):

void Main()
{
    var hold = "this is the thing. it returns true";
    var held = "this is the one that returns false";

    Console.WriteLine(One.Check(hold));
    Console.WriteLine(One.Check(held));

    Console.WriteLine(Two.Check(hold));
    Console.WriteLine(Two.Check(held));
}

// Define other methods and classes here
class One
{
    public static bool Check(string value)
    {
        return value != null && value.Contains('.');
    }
}

class Two
{
    public static Func<string, bool> Check = v => v != null && v.Contains('.');
}
Run Code Online (Sandbox Code Playgroud)

......它为两者产生了相同的IL.实质上:

XXX.Check:
IL_0000:  ldarg.0     
IL_0001:  brfalse.s   IL_000C
IL_0003:  ldarg.0     
IL_0004:  ldc.i4.s    2E 
IL_0006:  call        System.Linq.Enumerable.Contains
IL_000B:  ret         
IL_000C:  ldc.i4.0    
IL_000D:  ret         

XXX..ctor:
IL_0000:  ldarg.0     
IL_0001:  call        System.Object..ctor
IL_0006:  ret
Run Code Online (Sandbox Code Playgroud)

usr*_*usr 1

我无法想象该方法(显式编写的方法或 lambda 主体)的代码生成会存在差异的任何情况。C# 编译器中可能存在奇怪的(或设计的)极端情况,但这不是必需的。这种极端情况可能会出现,因为在发出程序集之前必须将 lambda 降级为方法。这个降低阶段可能会引入(希望是无关紧要的)变化。

关于 lambda code-gen 需要注意的要点是 lambda 可以关闭变量。这要求生成的方法成为生成的类的实例方法。在这里,您正在编写的 lambda 表达式永远不会导致这种情况。

在最近的 C# 编译器版本中,生成的方法是虚拟类型上的实例方法。这使得委托调用更快(这是不直观的,因为在这种情况下参数越多速度越快)。

调用这样的“假”静态方法是委托调用,.NET JIT 没有工具可以对其进行优化。(例如,运行时可以将调用站点专门化为已知的委托目标。JVM 为虚拟调用执行此操作。它通过虚拟调用进行内联。Hotspot JIT 非常先进。)这意味着您有间接调用开销,并且松散内联以及所有后续优化。

如果没有必要,切勿这样做。如果您想在运行时插入不同的方法,它可能会很有用。或者,也许这些静态Func变量可以充当缓存。或者,您可以在调试器中重新连接它们。

任何此类静态属性的访问get器都应该是零成本的,因为微小的方法是可靠内联的。

另一个轻微的性能缺点是类型加载和初始化时间增加。此外,更多的物体悬挂在周围会减慢所有未来 G2 收藏的速度。