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.
Related
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).
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.
When I write a Group By in query expression syntax, the compiler automatically picks Enumerable.GroupBy as my intended tareget method and I get an IEnumerable back instead of an IQueryable. That means a subsequent g.Sum call (inside of my Select) expects a Func(Of TSource, int) instead of an Expression(Of Func(Of TSource, int)). Is there a way to force the Group By to use Queryable.GroupBy instead and give me back an IQueryable?
Contrived Sample Code
Dim myQuery = From x In DataSource.Items
Group By x.Key Into g = Group
Select New With {
.Key = Key,
.RedItems = g.Sum(ItemsOfColor(Colors.Red)) '<== invalid because g.Sum expects a lambda
}
Private Function PurpleItems(color As Integer) As Expression(Of Func(Of Item, Integer))
Return Function(item) If(item.Color = color, 1, 0)
End Function
Why would I want to do this?
The compiler automatically converts between a lambda and an expression based on the target variable type (ie, both Dim f As Func(Of String, Integer) = Function(x) x.Length() and Dim e As Expression(Of Func(Of String, Integer)) = Function(x) x.Length() are valid) so there is no noticable difference in the code between an IEnumerable and IQueryable.
The problem is, LINQ to Entities (and I assume other db backed LINQ implementations) relies on expression trees to translate into SQL. That means the IEnumerable lambda version will not work against an IDbSet as I found in this old question.
The problem is that Queryable.GroupBy() returns IQueryable<IGrouping<TKey, TSource>>, where IGrouping<TKey, TSource> implemens IEnumerable<TSource>, but not IQueryable<TSource>.
And I believe your code wouldn't work anyway, because ItemsOfColor() wouldn't be actually called. Instead, the EF would get an expression that calls ItemsOfColor(). And since it doesn't know that method, it would throw an exception.
Is there a Visual Basic.NET method that can convert method parameters into an array?
For instance instead of:
Function functionName(param1 as object, param2 as object) as object
ArrayName = {param1, param2}
you could do something like:
Function functionName(param1 as object, param2 as object) as object
ArrayName = MethodThatGetsAllFunctionParams
Just curious really.
There is no way of doing that. The language itself doesn’t allow that. You can use reflection to get the currently executing method of the StackFrame of the execution. But even then it’s still impossible to retrieve the parameter values.
The only solution is to “patch” the applications by introducing point cuts into the method call. The linked answer mentions a possibility for that.
Take a look at ParamArrays. I think this solves what you're asking for?
http://msdn.microsoft.com/en-us/library/538f81ec%28v=VS.100%29.aspx
EDIT:
You could initialise a custom collection using your current function signature
Public Class CustomCollection(Of T)
Inherits System.Collections.Generic.List(Of T)
Sub New(param1 As T, param2 As T)
MyBase.New()
MyBase.Add(param1)
MyBase.Add(param2)
End Sub
End Class
and then call the function using
Dim result = functionName(New CustomCollection(Of Object)(param1, param2))
The Function signature would be changed to:
Public Function functionName(ByVal args As CustomCollection(Of Object)) As String
I'm still learing VB.NET and usually I just google my questions, but this time I really don't know what to look for so I'll try here.
Trying to write a function that takes the cache key as a parameter and returns the cached object. No problems there, but I can't figure out how to pass the type into the function to use with TryCast, so that I don't have to do that with the returned result.
Here is my function so far, the ??? is to be replaced with the type that is passed into the function somehow.
Public Function GetCache(ByVal tag As String) As Object
Dim obj As Object = Nothing
Dim curCache As Object = TryCast(System.Web.HttpContext.Current.Cache(tag), ???)
If Not IsNothing(curCache) Then
Return curCache
Else
Return Nothing
End If
End Function
Am I doing this completely wrong or am I just missing something?
Use a generic:
Public Function GetCache(Of T)(ByVal tag As String) As T
Return CType(System.Web.HttpContext.Current.Cache(tag), T)
End Function
update:
Edited to use CType, because trycast only works with reference types. However, this could throw an exception if the cast fails. You can either handle the exception, check the type before making the cast, or limit your code to reference types like this:
Public Function GetCache(Of T As Class)(ByVal tag As String) As T
Return TryCast(System.Web.HttpContext.Current.Cache(tag), T)
End Function