Func vs Function in VB

F appears as a Func(Of String) F2 as a Function() as string.

No, it doesn’t. In both cases the variable type is a delegate that’s equivalent to Func(Of String). Look at the code carefully again – both declarations have two parts; the declaration itself:

Dim F As Func(Of String)
' and
Dim F2

… and the initialisation:

Function() As String
    Return "B"
End Function

' and

Function() As String
    Return "B"
End Function

As you can see, the initialisation is identical. Merely the declaration is different because we omitted the explicit variable type in the second case. Thanks to Option Infer, the compiler can infer it for us.

In fact, the VB language specification has this to say (§8.4.2):

When an expression classified as a lambda method is reclassified as a value in a context where there is no target type (for example, Dim x = Function(a As Integer, b As Integer) a + b), the type of the resulting expression is an anonymous delegate type equivalent to the signature of the lambda method. [emphasis mine]

Finally, what, then, is the difference between Func and Function?

Function is a keyword that introduces a function – either anonymous function (lambda) as in your code, or a “normal” function having a name, as in this code:

Function F() As String
    Return "B"
End Function

Func(Of T…) on the other hand is a generic type for a delegate. A lambda or a delegate can be bound to a variable declared with a fitting Func type. This is happening in your code.


Func(Of TResult)() is a specific delegate with the name Func. It is a type declared inside the System namespace as follows:

Public Delegate Function Func(Of TResult)() As TResult

It could have been named differently. For instance:

Public Delegate Function MyParameterLessFunction(Of TResult)() As TResult

So Func is really just the name given to a delegate. Since the type of F2 is not specified explicitly, VB does not know a name for this delegate. Is it Func or MyParameterLessFunction or something else? Instead, VB just displays its signature Function() As String, since F2 does also fit a non-generic delegate declared as

Public Delegate Function AnonymousParameterLessStringFunction() As String

In your comment you use .ToString() on F and F2. This returns the run-time types, i.e., the types of the values assigned to these variables. These types can be different from the static types of these variables, i.e., the type given to the variable name. Let's make a little test

Imports System.Reflection

Module FuncVsFunction
    Dim F As Func(Of String) = Function() As String
                                   Return "B"
                               End Function
    Dim F2 = Function() As String
                 Return "B"
             End Function

    Sub Test()
        Console.WriteLine($"Run-time type of F:  {F.ToString()}")
        Console.WriteLine($"Run-time type of F2: {F2.ToString()}")

        Dim moduleType = GetType(FuncVsFunction)
        Dim fields As IEnumerable(Of FieldInfo) = moduleType _
            .GetMembers(BindingFlags.NonPublic Or BindingFlags.Static) _
            .OfType(Of FieldInfo)

        For Each member In fields
            Console.WriteLine($"Static type of {member.Name}: {member.FieldType.Name}")
        Next
        Console.ReadKey()
    End Sub
End Module

It displays

Run-time type of F:  System.Func`1[System.String]
Run-time type of F2: VB$AnonymousDelegate_0`1[System.String]
Static type of F: System.Func`1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
Static type of F2: System.Object

Note that F2 is simply typed as Object. This is a surprise. I expected it to be a of a delegate type.


You also see this difference in the debugger. If you set a break point into the Test method and then hover over the Dim keywords of F and F2, a popup displays

'Dim of F (static type)
Delegate Function System.Func(Of Out TResult)() As String

'Dim of F2 (static type)
Class System.Object

If you hover over the variable names

'F (run-time type)
Method = {System.String _Lambda$__0-0()}

'F2 (run-time type)
<generated method>

For F you not only get type information but also the name of the generated method itself. Since F2 is an Object, Visual Studio obviously does not dig as deep as for F.

Tags:

Vb.Net

Lambda