Cast to Func vs new Func?
Asked Answered
W

1

6

Is there any difference between the following two statement? They both work.

if ( ((Func<bool>)(()=>true))() ) { .... };
if ( new Func<bool>(()=>true)()) { .... };
Warhead answered 11/7, 2014 at 20:23 Comment(0)
L
12

No, they both compile to exactly the same IL.

It's easier to see if you actually give the lambda body something that depends on state - otherwise the compiler caches a single delegate instance for each lambda. But for example:

using System;

class Test
{
    bool value = DateTime.Now.Hour == 10;

    void Cast()
    {
        if (((Func<bool>)(() => value))())
        {
            Console.WriteLine("Yes");
        }
    }

    void New()
    {
        if (new Func<bool>(() => value)())
        {
            Console.WriteLine("Yes");
        }
    }

    static void Main()
    {
        new Test().Cast();
        new Test().New();
    }
}

Now the IL for Cast is:

.method private hidebysig instance void  Cast() cil managed
{
  // Code size       39 (0x27)
  .maxstack  2
  .locals init (bool V_0)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  ldftn      instance bool Test::'<Cast>b__0'()
  IL_0008:  newobj     instance void class [mscorlib]System.Func`1<bool>::.ctor(object,
                                                                                native int)
  IL_000d:  callvirt   instance !0 class [mscorlib]System.Func`1<bool>::Invoke()
  IL_0012:  ldc.i4.0
  IL_0013:  ceq
  IL_0015:  stloc.0
  IL_0016:  ldloc.0
  IL_0017:  brtrue.s   IL_0026
  IL_0019:  nop
  IL_001a:  ldstr      "Yes"
  IL_001f:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0024:  nop
  IL_0025:  nop
  IL_0026:  ret
} // end of method Test::Cast

and the IL for New is:

.method private hidebysig instance void  New() cil managed
{
  // Code size       39 (0x27)
  .maxstack  2
  .locals init (bool V_0)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  ldftn      instance bool Test::'<New>b__1'()
  IL_0008:  newobj     instance void class [mscorlib]System.Func`1<bool>::.ctor(object,
                                                                                native int)
  IL_000d:  callvirt   instance !0 class [mscorlib]System.Func`1<bool>::Invoke()
  IL_0012:  ldc.i4.0
  IL_0013:  ceq
  IL_0015:  stloc.0
  IL_0016:  ldloc.0
  IL_0017:  brtrue.s   IL_0026
  IL_0019:  nop
  IL_001a:  ldstr      "Yes"
  IL_001f:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0024:  nop
  IL_0025:  nop
  IL_0026:  ret
} // end of method Test::New

As you can see, they're same apart from the ldftn call, which just uses the appropriate compiler-generated method.

Lynch answered 11/7, 2014 at 20:24 Comment(4)
I'm curious why the compiler wouldn't use the same delegate instance here, since both anonymous functions have same captured variable ('value').Seringa
Why if ( (()=>true)() ) doesn't work and it needs to be casted? The compiler should be able to infer the type of Func<bool>?Warhead
@dc7a9163d9: Why? It could be any delegate type without any parameters and a return type which true can convert to.Lynch
@ErenErsönmez: It could, but the C# compiler basically doesn't check for that sort of thing. It would be a lot of work to check that two lambda expressions which look similar are actually equivalent, and there's not much benefit.Lynch

© 2022 - 2024 — McMap. All rights reserved.