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
.