How I can proceed each member of lambda expression with a Function that return value using delegate? - vb.net

I use Vb.net in my work. I find a difficulty when trying to delegate a Function that returns value into each member of lambda expression.
I can do if for each member of lambda is delegated into a Sub:
Public class TransactionX
Public Number1 as Integer
Public Number2 as Integer
end class
Public Class TestX
Public Sub DoTest()
Dim trans as New List(Of TransactionX)
trans.Add(New TransactionX with{.Number1=0, .Number2=0})
trans.Add(New TransacrtionX with{.Number1=1, .Number2=1})
dim changeNumber = New Action(Of TransactionX)(AddressOf DoChange)
dim transSearch = trans.Where(Function(t) t.Number1>0).ForEach(changeNumber)
end Sub
Private Sub DoChange(ByVal tranX as TransactionX)
with tranx
.Number1 = 10
.Number2 = 10
end with
end Sub
end Class
above code will update each member of lambda expression's result.
What if I want to do Number1 * Number2 for each member that I catch from Lambda with a private Function and then will return into a collection?
I think it can be done with C# but I could not solve it using Vb.net.

Change you method to return calculated value and use it with Select extension method
Private Function DoChange(ByVal tranX As TransactionX) As Integer
Return tranX.Number1 * tranX.Number2
End Function
Dim transSearch As IEnumerable(Of Integer) = trans.Select(AddressOf DoChange)
Use AddressOf keyword for passing existed method as parameter

Related

How to create a Boolean Function to list objects in a Generic Collection?

After creating a List(Of T), I want to create aBoolean function. First, we will ask data to add an object in the list. However, in case this new object has the same "DNI" (String attribute from the class Aspirante), then we cannot include this new Object in the list. Therefore, it should be True when we have an Object with the same attribute and False when we don´t, so we can add the new object.
Below is the code I did:
Public Class Oposicion
Private datos As New List(Of Aspirante)()
Public Function Alta(ByRef objAspirante As Aspirante) As Boolean
If datos.Contains(objAspirante.DNI) Then
Return True
Else
datos.Add(objAspirante)
Return False
End If
End Function
End Class
However it doesn´t work. I have no clue on how to do it. Sorry if I was not clear enough.
This doesn't answer your question directly but it involves a significant amount of code, so it won't work in a comment.
You probably shouldn't be using a List(Of T) in the first place. The HashSet(Of T) already includes functionality to prevent adding duplicate items, so that may be a better option. If you want to compare objects on a specific property value then you need to first create a comparer based on that:
Public Class Thing
Public Property Stuff As String
End Class
Public Class ThingComparer
Implements IEqualityComparer(Of Thing)
Public Overloads Function Equals(x As Thing, y As Thing) As Boolean Implements IEqualityComparer(Of Thing).Equals
Return x.Stuff.Equals(y.Stuff)
End Function
Public Overloads Function GetHashCode(obj As Thing) As Integer Implements IEqualityComparer(Of Thing).GetHashCode
Return obj.GetHashCode()
End Function
End Class
You then create a HashSet(Of T) that uses that comparer to determine equality:
Dim things As New HashSet(Of Thing)(New ThingComparer)
You can then just add items as you please by calling Add. That will either add the new item and return True or it will not not add the duplicate item and return False:
Dim variousStuff = {"One", "Two", "One"}
For Each stuff In variousStuff
Dim something As New Thing With {.Stuff = stuff}
If things.Add(something) Then
Console.WriteLine($"'{stuff}' was added successfully.")
Else
Console.WriteLine($"'{stuff}' is a duplicate and was not added.")
End If
Next
The potential drawback is that HasSet(Of T) does not implement IList(Of T), so you cannot access items by index. It does implement ICollection(OF T) though, so it does have a Count property and you can enumerate it with a For Each loop.
You can create a List(Of String) containing the just the DNI property of each object in datos. Then see if the DNI of objAspirante is contained in lst.
Public Class Oposicion
Private datos As New List(Of Aspirante)()
Public Function Alta(objAspirante As Aspirante) As Boolean
Dim lst As New List(Of String)
For Each a As Aspirante In datos
lst.Add(a.DNI)
Next
If lst.Contains(objAspirante.DNI) Then
Return True
Else
datos.Add(objAspirante)
Return False
End If
End Function
End Class
If you can change the type of datos, this might be easier.
Public Class Oposicion
Private datos As New Dictionary(Of String, Aspirante)()
Public Function Alta(objAspirante As Aspirante) As Boolean
If datos.ContainsKey(objAspirante.DNI) Then
Return True
Else
datos.Add(objAspirante.DNI, objAspirante)
Return False
End If
End Function
End Class
If you want to stick with your existing List(Of Aspirante), then simply use .Any() and pass it a Lambda to determine if one already exists. It'd look something like this:
Public Function Alta(ByVal objAspirante As Aspirante) As Boolean
If Not datos.Any(Function(x) x.DNI = objAspirante.DNI) Then
datos.Add(objAspirante)
Return True ' your description and code did not match for these!
End If
Return False ' your description and code did not match for these!
End Function
Note my comment on the Return lines...your code and description did not match here.

Lambda property value selector used in lambda expression

I sometimes find myself writing two versions of the same function that gets a count of members where one of several properties have a particular value. I've been looking at func and other examples to see if I could write a single function to do a count where a value matches one of several properties of an object. Feels like there should be a way...
Module Test
Private _students As New List(Of Student)
Sub Main()
_students.Add(New Student(1, "Stephen"))
_students.Add(New Student(2, "Jenny"))
' I'd like to replace the following lines...
Console.WriteLine(GetCountByID(1))
Console.WriteLine(GetCountByName("Stephen"))
' with a single function that could be used like below.
'Console.WriteLine(GetCountByType(1, Student.ID))
'Console.WriteLine(GetCountByType("Stephen", Student.Name))
Console.ReadLine()
End Sub
Public Function GetCountByID(ByVal id As Integer) As Integer
Return _students.Where(Function(s) s.ID = id).ToList.Count
End Function
Public Function GetCountByName(ByVal name As String) As Integer
Return _students.Where(Function(s) s.Name = name).ToList.Count
End Function
' I know this is wrong below but I'm just writing it like I'm thinking about it in my head
'Public Function GetCountByType(ByVal value As Object, selectorProperty As Func(Of Student)) As Integer
' Return _students.Where(Function(s) s.selectorProperty = value).ToList.Count
'End Function
Public Class Student
Public Property ID As Integer
Public Property Name As String
Public Sub New(ByVal id As Integer, ByVal name As String)
Me.ID = id
Me.Name = name
End Sub
End Class
End Module
You are along the right lines but your Func needed to return an object.
However it would be better to make it generic instead, the type needs to be of type IComparable so you can check for equality against the target value.
Public Function GetCountBy(Of T As IComparable)(selector As Func(Of Student, T), value As T) As Integer
Return _students.Where(Function(s) selector(s).CompareTo(value) = 0).Count()
End Function
Console.WriteLine(GetCountBy(Function(s) s.ID, 1))
Console.WriteLine(GetCountBy(Function(s) s.Name, "Stephen"))
p.s. your calls to ToList() are unnecessary
But once you have gone this far you might as well just pass in the complete predicate instead of a selector function and a value
Public Function CountWhere(predicate As Func(Of Student, Boolean))
Return _students.Where(predicate).Count()
End Function
Console.WriteLine(CountWhere(Function(s) s.ID = 1))
You can generalise this even further so it applies to any collection rather than just students, and make it an extension function if you wish
Public Function CountWhere(Of T)(coll As IEnumerable(Of T), predicate As Func(Of T, Boolean))
Return coll.Where(predicate).Count()
End Function
Console.WriteLine(CountWhere(_students, Function(s) s.ID = 1))

Why is this returning Index out of bound exception

When I get to the Read loop I get an index out of bounds error. I think its on the reader ordinal value, but I am not sure why I am getting it.
Private Function Create(Reader As SqlDataReader) As IEnumerable(Of MyObject)
SetOrdinals(MyObjectReader)
Dim MyObjects = New List(Of MyObject)
While MyObjectReader.Read()
Dim Temp = New MyObject() With {
.FirstValue = MyObjectReader.GetValue(Of Integer)(MyObjectReader(FirstValue_Ord)),
.SecondValue = If(MyObjectReader.GetValue(Of String)(MyObjectReader(SecondValue_Ord)), String.Empty).Trim(),
.ThirdValue = If(MyObjectReader.GetValue(Of String)(MyObjectReader(ThirdValue_Ord)), String.Empty).Trim(),
MyObjects.Add(Temp)
End While
Return MyObjects
End Function
Private Sub SetOrdinals(MyObjectReader As SqlDataReader)
FirstValueOrd = MyObjectReader.GetOrdinal("FirstValue")
SecondValue_Ord = MyObjectReader.GetOrdinal("SecondValue")
ThirdValue_Ord = MyObjectReader.GetOrdinal("ThirdValue")
End Sub
End Class
Public Module Extensions
<Extension>
Function GetValue(Of T)(rdr As SqlDataReader, i As Integer) As T
If rdr.IsDBNull(i) Then
Return Nothing
End If
Return DirectCast(rdr.GetValue(i), T)
End Function
End Module
You should just be passing in the ordinal to the GetValue calls:
While MyObjectReader.Read()
Dim Temp = New MyObject() With {
.FirstValue = MyObjectReader.GetValue(Of Integer)(FirstValue_Ord),
.SecondValue = If(MyObjectReader.GetValue(Of String)(SecondValue_Ord), String.Empty).Trim(),
.ThirdValue = If(MyObjectReader.GetValue(Of String)(ThirdValue_Ord), String.Empty).Trim()
}
MyObjects.Add(Temp)
End While
Here my version :)
Private Function Create(reader As SqlDataReader) As IEnumerable(Of MyObject)
Dim objects As New List(Of MyObject)()
Dim ordinals As New Ordinals(reader)
While reader.Read()
Dim Temp As New MyObject With
{
.FirstValue = reader.GetValueOrDefault(Of Integer)(ordinals.FirstValue),
.SecondValue = reader.GetValueOrDefault(ordinals.SecondValue, "").Trim(),
.ThirdValue = reader.GetValueOrDefault(ordinals.ThirdValue, "").Trim()
}
objects.Add(Temp)
End While
Return MyObjects
End Function
Private Class Ordinals
Public Property FirstValue As Integer
Public Property SecondValue As Integer
Public Property ThirdValue As Integer
Public Sub New(reader As SqlDataReader)
FirstValue = reader.GetOrdinal(nameOf(FirstValue))
SecondValue = reader.GetOrdinal(nameOf(SecondValue))
ThirdValue = reader.GetOrdinal(nameOf(ThirdValue))
End Sub
End Class
Public Module Extensions
<Extension>
Function GetValueOrDefault(Of T)(reader As SqlDataReader, ordinal As Integer) As T
Return reader.GetValueOrDefault(Of T)(ordinal, Nothing)
End Function
<Extension>
Function GetValueOrDefault(Of T)(reader As SqlDataReader,
ordinal As Integer,
defaultValue As T) As T
Dim value = reader(ordinal)
If value = DbNull.Value Then
Return defaultValue
End If
Return DirectCast(value, T)
End Function
End Module
Because extension method execute checking for DbNull.Value against already extracted object, we get rid from reading same value twice from SqlDataReader.
SqlDataReader.IsDbNull(index) reads value before checking for DbNull.
Extension method have two overloads:
- One which return default value of given type, if value is DbNull.Value. Nothing in vb.net is default value of type.
- And one which takes parameter for default value you want return if value is DbNull.Value. Possibility pass default value makes lines where you create new object shorter and more readable. We get rid of inline if statement.
Your extension method with name GetValue have "side effects". By name consumer of this method expect to get value from SqlDataReader. So he can expect to get DbNull.Value if database query return NULL, but instead he get null for string or 0 for integer. Name GetValueOrDefault is little bid more informative, so you don't need to go inside method to check what is doing.

VB: Problems with using variable from another class + what to do with not used interface`s functions

I have a problem with getting variable from another class and cannot understand what to do with interface`s functions which have already existed in another class.
What I have:
Form where clicking on a button I should see reversed string:
(I want to call pooraja.StringReverse which is below)
Private Sub btnPoora1_Click(sender As System.Object, e As System.EventArgs) _
Handles btnPoora1.Click
'Dim text As PrjTekstiPooraja.ITeisendused = New PrjTekstiPooraja.CtekstiPooraja
Dim text As PrjTekstiPooraja.ITeisendused = New PrjTekstiPooraja.CtekstiPooraja
Dim pooraja As PrjTekstiPooraja.ITeisendused = New PrjTekstiPooraja.CAlgrotimilinePooraja
text.strText = txtSisendTekst.Text
txtValjundTekst1.Text = pooraja.stringReverse
text.intStart = 1
text.intEnd = Len(txtSisendTekst.Text)
ascFSymbol.Text = text.ascFirstSymbol
ascLSymbol.Text = text.ascLastSymbol()
End Sub
CtekstiPooraja:
(Thiss class will be used to store data.Under data I mean strPooratavText. Data will be used in CAlgoritmilinePooraja)
Public Class CtekstiPooraja
Implements ITeisendused
Public intStartSymbol As Integer
Public intEndSymbol As Integer
Public strPooratavText As String
Private Property intEnd As Integer Implements ITeisendused.intEnd
Get
Return intEndSymbol
End Get
Set(ByVal value As Integer)
intEndSymbol = value
End Set
End Property
Private Property intStart As Integer Implements ITeisendused.intStart
Get
Return intStartSymbol
End Get
Set(ByVal value As Integer)
intStartSymbol = value
End Set
End Property
Public Function pooraText() As String Implements ITeisendused.pooraText
Return StrReverse(strPooratavText)
End Function
Public Property strText As String Implements ITeisendused.strText
Get
Return strPooratavText
End Get
Set(ByVal value As String)
strPooratavText = value
MsgBox(strPooratavText)
End Set
End Property
Public Sub teisendaText(ByRef strSisendText As String) Implements ITeisendused.teisendaText
strPooratavText = StrReverse(strSisendText)
End Sub
Public Function ascFirstSymbol() As String Implements ITeisendused.ascFirstSymbol
Return Asc(GetChar(strPooratavText, intStartSymbol))
End Function
Public Function ascLastSymbol() As String Implements ITeisendused.ascLastSymbol
Return Asc(GetChar(strPooratavText, intEndSymbol))
End Function
Public Function stringReverse() As String Implements ITeisendused.stringReverse
Return Nothing
End Function
End Class
CAlgrotimilinePooraja:
(This class will be called by form button. There I need to use stringReverse function with data from CtekstiPooraja. The problem is that everywhere is used the same interface and there is some functions and procedures from this interface which isnt necessary. I dont know what value should return these unused functions/procedures. Just using "return Nothing or return 0/ "" is bad idea, may be there is possible somehow referenceto to CTekstiPooraja functions/procedures variables")
Public Class CAlgrotimilinePooraja
Implements ITeisendused
Private x As New PrjTekstiPooraja.CtekstiPooraja
Public Function stringReverse() As String Implements ITeisendused.stringReverse
MsgBox(x.strPooratavText)
Dim i As Integer = 0
Dim j As Integer
Dim characters(j) As Char
Dim newString(j) As Char
characters = x.strPooratavText.ToCharArray()
newString = x.strPooratavText.ToCharArray()
Do While i <= j - 1
newString(i) = characters(j - 1)
newString(j - 1) = characters(i)
i += 1
j -= 1
Loop
Return newString
End Function
Public Function ascFirstSymbol() As String Implements ITeisendused.ascFirstSymbol
Return x.ascFirstSymbol()
End Function
Public Function ascLastSymbol() As String Implements ITeisendused.ascLastSymbol
Return Nothing
End Function
Public Property intEnd As Integer Implements ITeisendused.intEnd
Get
Return x.intEndSymbol
End Get
Set(ByVal value As Integer)
End Set
End Property
Public Property intStart As Integer Implements ITeisendused.intStart
Get
Return x.intStartSymbol
End Get
Set(ByVal value As Integer)
End Set
End Property
Public Function pooraText() As String Implements ITeisendused.pooraText
Return x.pooraText()
End Function
Public Property strText As String Implements ITeisendused.strText
Get
Return x.strPooratavText
End Get
Set(ByVal value As String)
End Set
End Property
Public Sub teisendaText(ByRef strSisendText As String) Implements ITeisendused.teisendaText
x.strPooratavText = StrReverse(strSisendText)
End Sub
End Class
MyInterface:
Public Interface ITeisendused
Property intStart As Integer
Property intEnd As Integer
Property strText As String
Function pooraText() As String
Function ascFirstSymbol() As String
Function ascLastSymbol() As String
Function stringReverse() As String
Sub teisendaText(ByRef strSisendText As String)
End Interface
I cannot understand how to get variable strPooratavText from CTekstiPooraja to CAlgrotimilinePooraja. Usually that instancewhich I create worked but not now. And I cannot understand what to do with already existed function and procedures in CAlgoritmilinePooraja when the same function and procedures has in another class. Maybe, it is possible to reference them somehow to existed functions/procedures in CTekstiPooraja? Could you explain me how to id, already tired to surf Internet to find a solution for it, have already try a lot.
Well, I think you have a fundamental problem with understanding interfaces. They describe data and behavior, it should be extremely rare to want to implement part of an interface.
That said, if you do want to implement part of an interface, instead of returning bogus data, throw an exception for behavior you don't implement.
Your specific problem is that CAlgoritmilinePooraja works on an instance of CtekstiPooraja, but it creates a new instance instead of using an existing one. Add
Sub New(incomingX as CtekstiPooraja)
x = incomingX
End Sub
to CAlgoritmilinePooraja. And then in your event, use....
Dim text As PrjTekstiPooraja.CtekstiPooraja = New PrjTekstiPooraja.CtekstiPooraja
text.strText = txtSisendTekst.Text
Dim pooraja As PrjTekstiPooraja.ITeisendused = New PrjTekstiPooraja.CAlgrotimilinePooraja(text)
That is the minimum change to your design that gets what you want to happen to happen but it's problably not what you should do. Other than implementing strReverse, CtekstiPooraja seems to be what you want, CAlgrotimilinePooraja looks to do just one thing, the actual string reversal.
I would move the implementation of strReverse into CtekstiPooraja, and then eliminate CAlgrotimilinePooraja.
PS I would try to stick to English for class names as well as functions and variables.

Create a new instance of a type given as parameter

I've searched for an answer and found some c#-examples, but could not get this running in vb.net:
I thought of something like the following:
public function f(ByVal t as System.Type)
dim obj as t
dim a(2) as t
obj = new t
obj.someProperty = 1
a(0) = obj
obj = new t
obj.someProperty = 2
a(1) = obj
return a
End Function
I know, I can create a new instance with the Activator.Create... methods, but how to create an array of this type or just declare a new variable? (dim)
Thanks in advance!
It really depends on the type itself. If the type is a reference type and has an empty constructor (a constructor accepting zero arguments), the following code should create an insance of it:
Using Generics:
Public Function f(Of T)() As T
Dim tmp As T = GetType(T).GetConstructor(New System.Type() {}).Invoke(New Object() {})
Return tmp
End Function
Using a type parameter:
Public Function f(ByVal t As System.Type) As Object
Return t.GetConstructor(New System.Type() {}).Invoke(New Object() {})
End Function
Personaly I like this syntax much more.
Public Class Test(Of T As {New})
Public Shared Function GetInstance() As T
Return New T
End Function
End Class
Or if you want to limit the possible types:
Public Class Test(Of T As {New, MyObjectBase})
Public Shared Function GetInstance() As T
Return New T
End Function
End Class