how to call a method with parameter Action(Of T) with variable - vb.net

I am in the process of converting some c# code to vb.net and I keep running into an issue with a particular method.
Here is the c# method signature -
public void DoSomething(Action<T> something)
{ .... do something in here }
Here is my vb.net conversion of the signature -
Public Sub DoSomething(ByVal something As Action(Of T))
....do something in here
End Sub
I have to call this with a variable. Here is a sample c# call -
_myobject.DoSomething(x => { newValue = x.CallSomeMethod() });
How would I perform this same call using Vb.Net?
I've tried this (along with a few variations), but the newValue object is always empty -
_myObject.DoSomething(Sub(x) newValue = x.CallSomeMethod())
I've also tried this -
_myObject.DoSomething(Function(x) newValue = x.CallSomeMethod() End Function)
If I do this -
_myObject.DoSomething(Function(x) newValue = x.CallSomeMethod())
I get an error message stating Cannot apply operator '=' to operands of type myType and myType

The SourceClass has the DoSomething method and the TargetClass has the CallSomeMethod that will be called as part of the anonymous Sub:
Public Class SourceClass
Public Sub DoSomething(ByRef something As Action(Of TargetClass))
Dim t As New TargetClass
something(t)
End Sub
End Class
Public Class TargetClass
Function CallSomeMethod() As Integer
Return 1000
End Function
End Class
In the Main method add this:
Public Module Module1
Sub Main()
Dim newValue = 11
Dim myObject = New SourceClass
myObject.DoSomething(New Action(Of TargetClass)(Sub(obj) newValue = obj.CallSomeMethod()))
Debug.WriteLine(newValue)
End Sub
End Module
In this example newValue will be assigned 1000.

Related

Detecting or preventing assignment operator to a class

Is there any way to make a class can be only initialized at declaration.
Public Class AnyValue
Private value As Int32
Public Sub New(ByVal aValue As Int32)
value = aValue
End Sub
End Class
'I want to be able to do this:
Dim val As New AnyValue(8)
'But not this.
val = New AnyValue(9)
Or it is possible to stop the assignment or detect when the operator = is used.
Lets just say this - No, you can't do what you want. The closest thing to it that I can think of, is to hide the constructor and give static access to the consumer as follows:
Public Class AnyValue
Private value As Int32
Private Sub New(ByVal aValue As Int32) ' Note - private constructor
value = aValue
End Sub
Public Shared Function Create(ByVal aValue As Int32) As AnyValue
Return New AnyValue(aValue)
End Function
End Class
'This will not work
Dim val As New AnyValue(8)
'This will not work
val = New AnyValue(9)
' This will work
Dim val As AnyValue = AnyValue.Create(8)
Now, if you look at this method of object creation, you can see that you can set all sort of rules for object construction. So, the client has very little input on the construction itself because how you construct the object is totally controlled by the object itself.

How should I store Child Classes in an enumerable and how to access them correctly?

Public Class UniqueList(Of T As BaseClass)
Inherits List(Of T)
Public Overridable Overloads Sub Add(value As T)
If Not Me.Contains(value) Then MyBase.Add(value)
End Sub
Public Function [Get](val As integer) As T
Return Me.Where(Function(cb) cb.Id = val)(0)
End Function
End Class
I get en error if I try to use it without casting it first, so I I cast it before trying to get my object :
dim mylist as new UniqueList(of Baseclass)
mylist.add(new ChildClass(1))
dim x as ChildClass= (mylist.get(1))
x.RandomMethod() ...
so I tried to create a new function in my UniqueList class that would cast it for me :
Public Function [GetChildClass](val As integer) As ChildClass
Return DirectCast(Me.Where(Function(cb) cb.Id = val)(0), ChildClass)
End Function
but I always get errors saying value T cannot be converted to ChildClass... is there any way to have this function returns me the correct object ?
edit: I cant change it to a list of ChildClass
...
edit : declare them however you want ...
Class BaseClass
public id as integer
public sub new(id as integer)
me.id = id
end sub
end class
Class ChildClass
inherits BaseClass
Public sub New(id as integer)
mybase.new(id)
end sub
public sub randomMethod()
'do nothing
end sub
end class
You may be looking for something like this:
Public Function [GetParent](val As integer) As ChildClass
Return DirectCast(Me.OfType(Of ChildClass).
Where(Function(cb) cb.id = val)(0), ChildClass)
End Function
Note - this will fail if no elements of type ChildClass are found, you can prevent it from failing by using .FirstOrDefault instead of manually indexing (0) into the first element. FirstOrDefault will return Nothing if no elements were found.

Pass a lambda to a generic function

I would like to create a generic class that can be used to contain items from a database table
As follows
Public Class RowCache(Of t)
Private items As New List(Of t)
Public Sub AddItem(item As t)
Me.items.Add(item)
End Sub
Public Function find(Filter As ?????) As t
????
End Function
End Class
Public Class useit
Public Sub test()
Dim ob As New RowCache(Of teamrow)
ob.find(func(rec) rec.teamcode)
Dim ob2 As New RowCache(Of employeerow)
ob2.find(func(rec) rec.employeeno)
End Sub
End Class
Is it possible to create the find method and if so how is it done
What goes in the parameter list
How do I use this in the find function
You could pass a lambda to the find function, which returns true if the item is found. You can combine this with Linq's SingleOrDefault to find the single item which is filtered.
Public Function find(filter As Func(Of t, Boolean)) As t
Return Me.items.SingleOrDefault(filter)
End Function
Then you can use it like this:
Dim result = ob.find(Function(rec) rec.teamcode = 1)

Calling Subroutines from lambda in vb.net

I find myself calling functions from lambdas frequently as the provided delegate does not match or does not have sufficient parameters. It is irritating that I cannot do lambda on subroutines. Each time I want to do this I have to wrap my subroutine in a function which returns nothing. Not pretty, but it works.
Is there another way of doing this that makes this smoother/prettier?
I have read that this whole lambda inadequacy will probably be fixed in VS2010/VB10 so my question is more out of curiosity.
A simple Example:
Public Class ProcessingClass
Public Delegate Sub ProcessData(ByVal index As Integer)
Public Function ProcessList(ByVal processData As ProcessData)
' for each in some list processData(index) or whatever'
End Function
End Class
Public Class Main
Private Sub ProcessingSub(ByVal index As Integer, _
ByRef result As Integer)
' (...) My custom processing '
End Sub
Private Function ProcessingFunction(ByVal index As Integer, _
ByRef result As Integer) As Object
ProcessingSub(index, result)
Return Nothing
End Function
Public Sub Main()
Dim processingClass As New ProcessingClass
Dim result As Integer
' The following throws a compiler error as '
' ProcessingSub does not produce a value'
processingClass.ProcessList( _
Function(index As Integer) ProcessingSub(index, result))
' The following is the workaround that'
' I find myself using too frequently.'
processingClass.ProcessList( _
Function(index As Integer) ProcessingFunction(index, result))
End Sub
End Class
If you find that you are doing it too often and generally with the same type of data, you can wrap the delegate in a class.
Create a base class that converts to the delegate:
Public MustInherit Class ProcessDataBase
Public Shared Widening Operator CType(operand As ProcessDataBase) as ProcessingClass.ProcessData
Return AddressOf operand.Process
End Sub
Protected MustOverride Sub Process(index As Integer)
End Class
Inherit from the class:
Public Class ProcessResult
Inherits ProcessDataBase
Public Result As Integer
Protected Overrides Sub Process(index as Integer)
' Your processing, result is modified.
End SUb
End Class
Use it:
Public Class Main()
Public Sub Main()
Dim processingClass As New ProcessingClass
Dim processor As New ProcessResult
processingClass.ProcessList(processor)
Dim result as integer=processor.Result
End Sub
End Class
It IS fixed in VB10, the VS10 Beta is available, if it's an option for you to use it. In VB10 you have lambdas without a return value, and inline subs/functions.
For now, maybe you could just forget lambdas and work with delegates instead? Something like:
processingClass.ProcessList(AddressOf ProcessingSub)

Generic List Equivalent of DataTable.Rows.Find using VB.NET?

I am converting DataTables to a generic list and need a quick and easy way to implement a Find function. It seems I am going to have to use a Predicate. Upon further investigation, I still can't seem to re-create the functionality. I have this predicate...
Private Function ByKey(ByVal Instance As MyClass) As Boolean
Return Instance.Key = "I NEED THIS COMPARISON TO BE DYNAMIC!"
End Function
And then calling it like this...
Dim Blah As MyClass = MyList.Find(AddressOf ByKey)
But I have no way to pass in a key variable to this predicate to do the comparison, as I used to do with DataTable...
Dim MyRow as DataRow = MyTable.Rows.Find(KeyVariable)
How can I setup a predicate delegate function in VB.NET to accomplish this?
Do not recommend LINQ or lambdas because this is question is regarding .NET version 2.0.
Just put your predicate in a class instance:
Public Class KeyMatcher
Public Sub New(ByVal KeyToMatch As String)
Me.KeyToMatch = KeyToMatch
End Sub
Private KeyToMatch As String
Public Function Predicate(ByVal Instance As MyClass) As Boolean
Return Instance.Key = KeyToMatch
End Function
End Class
and then:
Dim Blah As MyClass = MyList.Find(AddressOf New KeyMatcher("testKey").Predicate)
We can even get a little fancy and make this generic:
Public Interface IKeyed(Of KeyType)
Public Key As KeyType
End Interface
Public Class KeyMatcher(Of KeyType)
Public Sub New(ByVal KeyToMatch As KeyType)
Me.KeyToMatch = KeyToMatch
End Sub
Private KeyToMatch As KeyType
Public Function Predicate(ByVal Instance As IKeyed(Of KeyType)) As Boolean
Return Instance.Key = KeyToMatch
End Function
End Class
And then make your MyClass type implement the new IKeyed interface