Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is a static function equivalent to a static Func member in C#?

It looks like a static method is the same as static Func field. Am I missing something, or are they essentially interchangeable (same footprint, etc)?

A static property ends up the same as the other two example, except it includes the (minimal) overhead of the "get" accessor.

Maybe a bit pointless and navel-gazing to ask… but I like to understand what's going on "under the covers", even when it's not immediately relevant.

To be sure: I'm not about to switch all my static methods to lambda expressions (and drive my coworkers crazy). But, there might be some reasonable scenario where a static variable makes more sense than writing a method. Or maybe the opposite: convincing someone to use static methods instead of lambda expressions to make the code more readable or whatever

Plus, I'm curious if there's a better way to investigate these kinds of questions

My test

I put this simple example into LINQPad (v4.57.02, "compile with /optimize+" enabled):

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('.');
}

… and it generated the same IL for both. Essentially:

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
like image 976
David J Avatar asked Jan 26 '16 22:01

David J


1 Answers

I cannot think of any case where there would be a difference in code-gen for the method (the explicitly written method or the lambda body). There might be a weird (or by-design) corner case in the C# compiler but that's not essential. Such corner cases can arise because lambdas must be lowered into methods before emitting an assembly. This lowering stage might introduce (hopefully inconsequential) changes.

The main point to note about lambda code-gen is that lambdas can close over variables. This requires the generated method to become an instance method on a generated class. Here, the form of the lambdas you're writing will never cause this.

In recent C# compiler versions the generated method is an instance method on a dummy type. This makes delegate calls faster (which is unintuitive because more arguments are faster in this case).

Invoking such a "fake" static method is a delegate call and the .NET JIT has no facilities to optimize that away. (It could, for instance, runtime specialize the call site to a known delegate target. The JVM does this for virtual calls. It inlines right through virtual calls. The Hotspot JIT is very advanced.) This means that you have indirect call overhead and loose inlining and also all follow-up optimizations.

If not necessary this should never be done. It might be useful if you want to plug in different methods at runtime. Or, maybe these static Func variables can act as a cache. Or, you can rewire them in the debugger.

The get accessor of any such static property should be exactly zero-cost because tiny methods are reliably inlined.

Another slight performance drawback is increased type load and initialization time. Also, more objects hanging around slowing down all future G2 collections.

like image 196
usr Avatar answered Sep 22 '22 08:09

usr