Func vs Function in VB - vb.net

Would anyone be able to tell me what is the difference between a Func and a Function in VB.
For instance see the following:
Dim F As Func(Of String) = Function() As String
Return "B"
End Function
Dim F2 = Function() As String
Return "B"
End Function
F appears as a Func(Of String)
F2 as a Function() As String.
It looks like they do the same thing but given that the compiler sees them as having different types surely there must be a subtlity.
Best regards
Charles

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.

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 is a delegate type - System.Func.
Function is a keyword used to create lambda expressions.
The above lambda expressions are of type System.Linq.Expression(Of System.Func(Of String)) and are indeed implicitly convertible to the delegate type Func(Of String).
The difference is that the expression is stored as an expression tree - describing the structure of the code. During the implicit conversion it is actually compiled into a delegate.

Related

Call a generic function passed as parameter in recursive function

My desire is to run a given function by name through AddressOf with one input parameter, e.g. Function Foo(x as Integer) As Integer. The two inputs I need into the recursive function are the function name _name As String and an object of some type t _list As t (Integer, Double, List(Of Integer), etc). The goal is to process either an element or list of elements with the function name, as there are multiple times I need to process a list by a given function and I do not wish to replicate the list processing code in each location. The ways I've tried to call my best go at this type of function (below) that didn't crash completely resulted in this error:
Warning: List.Test operation failed. Overload resolution failed because no Public 'ProcessList' can be called with these arguments:
'Public Shared Function ProcessList(Of t)(_func As Func(Of Object,t), _list As System.Object) As IEnumerable(Of t)':
Type argument inference fails for argument matching parameter '_func'.
Iterator Function ProcessList(Of t)(_func As Func(Of Object, t), _list As Object) As IEnumerable(Of t)
If _list.GetType = GetType(List(Of t)) Then
Yield _list.SelectMany(Function(l) ProcessList(_func, l))
Else
Yield _func(_list)
End If
End Function
For reference, I found a snippet of Python code that effectively does what I need, but I'm a little rusty on translating in this direction (Python to VB.net), and I'm not as familiar with this type of programming in VB.net. The Python snippet is:
def ProcessList(_func, _list):
return map(lambda x: ProcessList(_func, x) if type(x)==list else _func(x), _list)
Any help as to how I need to call this function, or how to rework this function if my approach is flawed, would be greatly appreciated!
Update:
I re-examined how I was calling the function and a few other things based on #djv's info that my method is working. First, due to the nature of how I'm interfacing with these functions, I have to expose the above function with:
Public Shared Function Foo(ByVal _input As Object) As Object
Return Utilities.ProcessList(AddressOf Bar, _input)
End Function
I'm also now getting the error message:
Warning: List.Test operation failed.
Unable to cast object of type 'System.Int32' to type 'System.Collections.Generic.IList`1[System.Int32]'.
The issue at this point probably lies with the method in which I'm calling my ProcessList function, rather than the function itself as I thought. I'm interfacing with a GUI that is not happy with calling ProcessList on its own, so I need this intermediate "helper" function, which I am apparently not using correctly.
You will always get an IEnumerable(Of T) and T can either be a primitive (i.e. Integer) or list of primitive (i.e. List(Of Integer)). So when you try to call it with a List, you get a List(Of List(Of Integer)) for example.
We can see why by breaking ProcessList up into two methods. The difference between them is the type of the second argument which is either T or IEnumerable(Of T)
Sub Main()
Dim i As Integer = 1
Dim li As New List(Of Integer) From {1, 1, 1}
Dim ri As IEnumerable(Of Integer) = ProcessList(AddressOf foo, i).ToList()
Dim rli As IEnumerable(Of Integer) = ProcessList(AddressOf foo, li).ToList()
Dim d As Double = 1.0#
Dim ld As New List(Of Double) From {1.0#, 1.0#, 1.0#}
Dim rd As IEnumerable(Of Double) = ProcessList(AddressOf foo, d).ToList()
Dim rld As IEnumerable(Of Double) = ProcessList(AddressOf foo, ld).ToList()
Console.ReadLine()
End Sub
Function ProcessList(Of T)(f As Func(Of T, T), p As IEnumerable(Of T)) As IEnumerable(Of T)
Return p.Select(Function(i) ProcessList(f, i)).SelectMany(Function(i) i)
End Function
Iterator Function ProcessList(Of T)(f As Func(Of T, T), p As T) As IEnumerable(Of T)
Yield f(p)
End Function
Function foo(param As Integer) As Integer
Return param + 1
End Function
Function foo(param As Double) As Double
Return param + 1.0#
End Function
Previously, I could not even hit the line in your original code which did the SelectMany. Now, it is hit when the proper function is called. I also retooled that call to fit the new function signature.
The overloads are both called, based on the second argument passed them. However, you only need one foo method for each T (either a primitive or its IEnumerable).

Generic Function and action/return depending on Type

I have a function which deserializes some custom serialization sent by an API.
I want to build a generic function so that the deserialized object is not of type Object but of the correct type.
The strings which contain the serialized object can be deserialized into one of the following types:
A String,
an IList(Of String),
an IDictionnary(Of String),
one of many SomeNameContainer classes, all derived from a
BaseContainer class,
an IList(Of SomeNameContainer), or
an IDictionnary(Of SomeNameContainer).
I would like to have a single Function Deserialize(Of T)(MyString as String) as T.
Inside this function, I tried to run some Select Case T: GetType(String):Etc tests in order to separate the different actions to run on MyString, depending on the expected object to create from the deserialization.
For example, deserializing into a SomeNameContainer is normally done via another generic function: Dim Deserialized as SomeNameContainer = GetFromContainer(SomeNameContainer)(MyString)
However, I get quickly limited, mainly because:
I cannot return a String type, because it is unable to cast it
into T.
String is a value type, whilst SomeNameContainer are classes. So it is not possible to add an (Of T As {New}) constraint. Which means I am unable to do something like Dim NameContainer as New T: If TypeOf NameContainer Is BaseContainer in order to apply the same operation to all the classes derived from BaseContainer.
One track I have found is to use CTypeDynamic(Of T)(obj as object), which casts at run-time. That might fix problem 1, but problem 2 is still on.
Function Deserialize(Of T)(MyString as String) as T
Select Case GetType(T)
Case GetType(String)
Return SomeFunction(String) '<- Only run-time casting allowed: Return CTypeDynamic(Of String)(SomeFunction(String))
Case GetType(IList(Of String)
Return SomeOtherFunction(String)
Case GetType(...)
'...
Case Else
Dim MyContainer as New T '<- Not Allowed to use New
if TypeOf MyContainer Is T then
Return GetFromContainer(Of T)(String)
else
'...
End If
End Select
End Function
I could decide to split each Type into a separate function. I would like to avoid so that I do not end up with 6 functions. That is because I also need to run some other operations on the string before it is deserialized. For the story, the strings come under various encoding/encryption formats. So if I have 4 formats, that is now 4x6=24 functions I would need to deal with.
I would love to have the luxury of encapsulating all the decoding/deserialization into a single function: Dim MyObject as Something = Deserialize(Of Something)(StringFromAPI, MyEncodingEnumOptions.Option42)
Many thanks in advance!
Performing a specific action depending on the type of a specific variable: that feels similar to Overloading, except that here instead of performing the action based on the type of the input variables, it should be base on the type of the output variables.
Unfortunately, it is not possible to overload the TypeName of a generic function. For example, Function MyFunction(Of T as New)(SomeParameter as String) as T and Function MyFunction(Of T as Structure)(SomeParameter as String) as T cannot coexist in the same namespace.
An alternative is to pass the expected output type as an input argument, so that regular overloading can be performed: Sub MyFunction(ByVal SomeParameter as String, ByRef OutputVar as SomeType). Each overload including a different SomeType TypeName.
The output of the "function" is stored into OutputVar, which is passed ByRef and retrieved after running the Sub:
Dim MyObject as Something = Deserialize(Of Something)(StringFromAPI, MyEncodingEnumOptions.Option42)
Becomes
Sub Deserialize(ByRef MyObject as String, ByVal MyString As String, ByVal EncodingOption As MyEncodingEnumOptions)
MyString = SomeDecoding(MyString, EncodingOption)
MyObject = SomeFunction(MyString)
End Sub
Sub Deserialize(ByRef MyObject as IList(Of String), ByVal MyString As String, ByVal EncodingOption As MyEncodingEnumOptions)
MyString = SomeDecoding(MyString, EncodingOption)
MyObject = SomeOtherFunction(MyString)
End Sub
'...
Dim MyObject as Something
Deserialize(MyObject, StringFromAPI, MyEncodingEnumOptions.Option42)
'Now MyObject has been filled with the relevant data.
An alternative is to use late binding / runtime object initilization, using Activator.CreateInstance(Of T). A typical switch over T would then look like:
Public Function GetDeserializedObject(Of T)(ByVal MyString As String) As T
Select Case GetType(T)
Case GetType(String)
Return CTypeDynamic(MyString, GetType(T)) '<-- Runtime Casting
Case Else
If Not MyString.IsDeserializable Then Throw New ArgumentException(String.Format("Unable to deserialize to a {0} object: The provided string is not valid.", GetType(T).ToString))
Select Case GetType(T)
Case GetType(IList(Of String))
Return CollectionString.ToStringList(MyString)
Case Else
Dim MyReturn As T = Activator.CreateInstance(Of T) '<-- Object instantiation to the type provided at Runtim
If TypeOf MyReturn Is BaseContainer Then '<-- Now we can use TypeOf ... Is ... which will return True for all Object derived from BaseContainer
Return Activator.CreateInstance(GetType(T), MyString)
ElseIf TypeOf MyReturn Is IList(Of BaseContainer) Then
Dim MyCollectionString As CollectionString = MyString
Return MyCollectionString.ExportToContainerList(MyReturn.GetType)
Else
Throw New ArgumentException(String.Format("Unable to deserialize to a {0} object: This type of object is not supported.", GetType(T).ToString))
End If
End Select
End Select
End Function

Overloads New Method With Args

I've been build a large application that requires the need to overload the NEW method with args (Common I guess).
I have lots of properties in some classes and most if not all get overloaded in the new method. I wondered if there is a way to capture the arguments from the method and assign the values to the custom object by looking through the properties/args. All my property name within the class match the ones being passed as args in the method so matching the "name" properties should be possible???
OR a less taxing way of manually typing me.property = arg for each and every argument...
for example:
public class myClass
property arg1 as string
property arg2 as string
property arg3 as string
public sub new(arg1 as string, arg2 as string, arg3 as string)
For each arg in methodbase.getcurrentmethod.getParameters
custObj(<property name>).value = arg.value where custObj.name = arg.name
end for
end sub
end class
This isn't valid VB.net syntax is just an example of what I'm trying to achieve.
There is no real solution to your problem because reflection can not get a parameters value. But:
You can hack your way around this by creating an anonymous type inside
your method and taking advantage of projection initialisers. You can
then interrogate the anonymous type's properties using reflection.
https://stackoverflow.com/a/1868507/4035472
The result could be something like this:
Class [MyClass]
Public Property arg1 As String
Public Property arg2 As String
Public Property arg3 As String
Public Sub New(arg1 As String, arg2 As String, arg3 As String)
Dim hack = New With { arg1, arg2, arg3}
Dim all = BindingFlags.Instance Or BindingFlags.Public Or BindingFlags.NonPublic
Dim props = GetType([MyClass]).GetProperties(all).ToDictionary(Function(x) x.Name, Function(x) x)
For Each p As PropertyInfo In hack.GetType().GetProperties()
props(p.Name).SetValue(Me, p.GetValue(hack))
Next
End Sub
End Class

VB.NET Calling BeginInvoke on another thread

So from the comments section where this persons code was translated to VB.NET on http://www.codeproject.com/KB/cs/Threadsafe_formupdating.aspx it shows a little code to aid in calling cross thread UI stuff.
<System.Runtime.CompilerServices.Extension()> _
Public Function SafeInvoke(Of T As ISynchronizeInvoke, TResult)(ByRef isi As T, ByRef [call] As Func(Of T, TResult)) As TResult
If isi.InvokeRequired Then
Dim result As IAsyncResult = isi.BeginInvoke([call], New Object() {isi})
Dim endResult As Object = isi.EndInvoke(result)
Return DirectCast(endResult, TResult)
Else
Return [call](isi)
End If
End Function
When I try to call the following however I get an error:
Me.SafeInvoke(Function(x) x.Close())
or
frmLobby.SafeInvoke(Function(x) x.Close())
Error 1 Data type(s) of the type parameter(s) in extension method 'Public Function SafeInvoke(Of TResult)(ByRef call As System.Func(Of frmLogin, TResult)) As TResult' defined in 'GvE.Globals' cannot be inferred from these arguments. Specifying the data type(s) explicitly might correct this error. C:\GvE\GvE\frmLogin.vb 37 9 GvE
What am I missing? I'm calling that code from inside a method defined in a form but that method is being called from another thread.
Just trying to avoid delegates and this is what the code above is supposed to do, but just can't get it to work.
Thanks
Your SafeInvoke method takes a Func(Of T, TResult).
That's a function that takes a T and returns a TResult.
Since x.Close() is a Sub and doesn't return anything, you can't make it into a Func(Of T, TResult).
You should make an overload that takes an Action(Of T) – a sub that takes a T and doesn't return anything.

How to properly use Nullable w/ numeric types in constructors?

In VB.NET, I have a class that implements a range of numbers, call it NumericRange(Of T). Internally, NumericRange stores T as a Nullable, T?. I have another class that wraps this class as NumericRange(Of UInt16). Call this class MyNumRange (I'm being simplistic here).
So In MyNumRange, I have a few constructors defined:
Public Sub New(ByVal num1 As UInt16?, ByVal num2 As UInt16?)
' Code
End Sub
Public Sub New(ByVal num As UInt16, ByVal flag As Boolean)
' Code
End Sub
Public Sub New(ByVal num As UInt16)
' Code
End Sub
In some code outside of MyNumRange, I try to instantiate an open-ended range. That is, a range value where one of the operands is missing to represent a greater-than-or-equal to scenario. I.e., Calling New MyNumRange(32000, Nothing) should equate (after calling MyNumRange's overridden ToString method) to 32000 ~ (note the trailing space, and assume ~ is the delimiter).
Except, calling New MyNumRange(32000, Nothing) doesn't jump to the constructor with a signature of New(UInt16?, UInt16?), but to New(UInt16?, Boolean) instead. This causes NumericRange to process the number 32000 as a single, specific value, not the open-ended range.
My question is, how can I use the constructors as I have them defined above in such a way that I can pass a Nothing value to the second argument of the New(UInt16?, UInt16?) constructor, it gets translated into Nothing, and num2.HasValue, if called from within the constructor, would report False?
Do I need to rethink how I have my constructors set up?
The default constructor of Nullable<T> can be utilized. When called as new Nullable<UInt16>(), it will act as a nullable with no value. In VB terms, you should be able to do New Nullable(of UInt16)().
DirectCast(Nothing, UInt16?) will give you the value you want to pass in, but doing so produces a compiler error:
Overload resolution failed because no accessible 'New' can be called without a narrowing conversion:
'Public Sub New(num As UShort, flag As Boolean)': Argument matching parameter 'num' narrows from 'Short' to 'UShort'.
'Public Sub New(num As UShort, flag As Boolean)': Argument matching parameter 'flag' narrows from 'UShort?' to 'Boolean'.
'Public Sub New(num1 As UShort?, num2 As UShort?)': Argument matching parameter 'num1' narrows from 'Short' to 'UShort?'.
However, it works fine if you use pass in an explicitly-typed value:
Dim num1 As UInt16? = 32000S
Dim r = New MyNumRange(num1, DirectCast(Nothing, UInt16?))