I wonder if there is a way to make a class instance that accepts parameters and generate results according to this parameter.
This is very common in VB.net built-in classes, but I wonder how to make it myself
Dim myNumbers as new NUMBERS
myNumbers.First = 1
myNumbers.Second = 2
Msgbox(myNumbers(1,2,3,4,5,5).max)
In the above code myNumber is a class which accepts some numbers and return the max.
You can use Default properties in order to achieve that. If you don't want it to be Set-able you can just mark it as ReadOnly, and if you don't want to return a specific value just return Nothing.
Returning something from the property:
Default Public ReadOnly Property Calculate(ByVal a As Integer, ByVal b As Integer, ByVal c As Integer) As Integer
Get
Dim d As Integer = a * b + c + Me.First
DoSomeStuff(d)
Return d * Me.Second
End Get
End Property
Returning nothing:
Default Public ReadOnly Property Calculate(ByVal a As Integer, ByVal b As String) As Object
Get
Dim c As String = DoStuff()
DoSomeOtherStuff(a, b, Me.First, Me.Second, c)
Return Nothing
End Get
End Property
Usage example:
'With return value.
Dim num As Integer = myNumbers(34, 5, 13)
'Ignoring return value.
Dim dummy As Object = myNumbers(64, "Hello World!")
'Ignoring return value.
Dim dummy As Object = myNumbers.Calculate(13, "I am text")
The only backside with this is that you must do something with the returned value (for instance assign it to a variable). Simply doing:
myNumbers(64, "Hello World!")
doesn't work.
Related
I'm trying to calculate the total count and value of orders for a customer, using the code below.
Dim iOrderCount As Integer
Dim iLineCount As Integer
Dim cTotalGoods As Decimal
Dim cTotalVat As Decimal
Dim cTotalDelivery As Decimal
Using manOrders As New CManOrders(m_dbSql)
manOrders.GetOrdersInProgress(sAccountCode, iOrderCount, iLineCount, cTotalGoods, cTotalVat, cTotalDelivery)
When I assign values to these variables in the GetOrdersInProgress subroutine, the values are being assigned correctly, which I can see when I step through the code.
Public Sub GetOrdersInProgress(sAccountCode As String, ByRef RET_orderCount As Integer, ByRef RET_lineCount As Integer,
ByRef RET_totalGoods As Decimal, ByRef RET_totalVat As Decimal, RET_totalDelivery As Decimal)
...
For Each dr As DataRow In m_dbSql.getDataTable(sql).Rows
RET_orderCount = dbToInt(dr(ORDER_COUNT))
RET_lineCount = dbToInt(dr(LINE_COUNT))
RET_totalGoods = dbToDecimal(dr(TOTAL_GOODS))
RET_totalVat = dbToDecimal(dr(TOTAL_VAT))
RET_totalDelivery = dbToDecimal(dr(2))
Return
Next
However, once I step through and move back into where the GetOrdersInProgress subroutine is called from, all of the values in the variables are returned correctly, except for RET_totalDelivery - the new one I've added to another developer's project.
The value in the RET_totalDelivery variable in the Public Sub GetOrdersInProgress... line is correct and it's correct after the assignment, but when it reaches Return and the variables are then used in the parent subroutine, for some reason they're all correct except for the new one I've added, RET_totalDelivery. I'd understand if the value wasn't being assigned correctly, however it is.
Why would it be returning 0 all the time?
By default, arguments passed to vb.net methods are passed by value, or ByVal. You did not specify ByRef in your RET_totalDelivery argument in GetOrdersInProgress.
Changes made to arguments passed by value are not retained when the method ends.
Your Sub should now be...
Public Sub GetOrdersInProgress(sAccountCode As String, ByRef RET_orderCount As Integer, ByRef RET_lineCount As Integer, ByRef RET_totalGoods As Decimal, ByRef RET_totalVat As Decimal, ByRef RET_totalDelivery As Decimal)
I prefer to write the method as a function. Write a class to hold all the return values. Then change the method into a function with just input parameters and return the values you fetch from sql.
Sub Main
Dim bl = New OrdersInProgressBusinessLogic()
Dim ordersInProgress = bl.GetOrdersInProgress("some account code")
End Sub
Public Class OrdersInProgress
Public Property OrderCount As Integer
Public Property LineCount As Integer
Public Property TotalGoods As Decimal
Public Property TotalVat As Decimal
Public Property TotalDelivery As Decimal
End Class
Public Class OrdersInProgressBusinessLogic
Public Function GetOrdersInProgress(sAccountCode As String) As OrdersInProgress
Dim ordersInProgress = New OrdersInProgress()
' some code here to fetch data from sql
For Each dr As DataRow In m_dbSql.getDataTable(sql).Rows
With ordersInProgress
.OrderCount = dbToInt(dr(ORDER_COUNT))
.LineCount = dbToInt(dr(LINE_COUNT))
.TotalGoods = dbToDecimal(dr(TOTAL_GOODS))
.TotalVat = dbToDecimal(dr(TOTAL_VAT))
.TotalDelivery = dbToDecimal(dr(2))
End With
Next
Return ordersInProgress
End Function
' some other functions/subs for OrdersInProgress class
End Class
I can't understand what is happening with the following code in VB.NET. When I run this code:
Public Function test() As Boolean
Dim a As Integer = 1
Dim b As Object = a
Dim c As Object = b
Return Object.ReferenceEquals(b, c)
End Function
Then the function returns True. However, if I run this:
Structure TTest
Dim i As Integer
Dim tid As Integer
Sub New(ByVal _i As Integer, ByVal _tid As Integer)
i = _i
tid = _tid
End Sub
End Structure
Public Function test_2() As Boolean
Dim a As New TTest(1, 1)
Dim b As Object = a
Dim c As Object = b
Return Object.ReferenceEquals(b, c)
End Function
Then it returns False. In both functions, I declare two value type variables, an Integer on the first and a custom Structure on the second one. Both should be boxed upon object assignment, but in the second example, it seems to get boxed into two different objects, so Object.ReferenceEquals returns False.
Why does it work this way?
For primitive types, .Net is able to re-use the same "box" for the same values, and thus improve performance by reducing allocations.
Same with strings, it's .NET way to optimize thing. But as soon as you use it, the reference will change.
Sub Main()
Dim a As String = "abc"
Dim b As String = "abc"
Console.WriteLine(Object.ReferenceEquals(a, b)) ' True
b = "123"
Console.WriteLine(Object.ReferenceEquals(a, b)) ' False
Console.ReadLine()
End Sub
What I want is to return 2 values from the database with a function and then store the values in variables so I can work with them. This is my code.
Function Buscar_Registro(ByVal xId As Integer) As String
Dim a, b As String
'convertir cadena
Dim Id As Integer
Id = xId
'conexión
Dim Conexion As OleDbConnection = New OleDbConnection
Conexion.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=D:\Visual\2000Phrases\2000 Phrases.accdb"
'cadena SQL
Dim CadenaSQL As String = "SELECT * FROM Data WHERE Id = " & Id
'Adaptador
Dim Adaptador As New OleDbDataAdapter(CadenaSQL, Conexion)
'Data set
Dim Ds As New DataSet
'Llenar el Data set
Conexion.Open()
Adaptador.Fill(Ds)
Conexion.Close()
'Contar registro
If (Ds.Tables(0).Rows.Count = 0) Then
Return False
Else
a = Ds.Tables(0).Rows(0)("Nombre").ToString()
b = Ds.Tables(0).Rows(0)("Apellido").ToString()
Ds.Dispose()
Return a
Return b
Return True
End If
End Function
Private Sub Button1_Click_1(sender As Object, e As EventArgs) Handles Button1.Click
Randomize()
Dim value As Integer = CInt(Int((20 * Rnd()) + 1))
TextBox3.Text = Buscar_Registro(value)
TextBox4.Text =
End Sub
I dont' know how to do it. The function returns only the value of "a"
Thanks
To return more values you need to change your function "as object"
Function Buscar_Registro(ByVal xId As Integer) As Object
and then you can put your return values into an object this way:
Return{a, b, true}
You'll get your values this way:
Dim mObj as object = Buscar_Registro(yourInteger)
you'll have:
a in mObj(0)
b in mObj(1)
True in mObj(2)
adapt it to your needs
EDIT (message to those that downvoted):
Creating a class an using a specific Object (the one created) to make a Function able to return multiple elements is surely the best choice.
Anyway, if someone doesn't know that it's possible to use the method that I showed in my answer, he is probably not (yet) able to create a class. So I think it's better give an usable (but not perfect) answer instead of a perfect (but unusable for the one who asked) answer.
This is what I think. Anyone can think differently.
Your best option here is to create your own class with the data you need and return that.
Public Class Data
Public Property Nombre As String
Public Property Apellido As String
End Class
And then do:
Function Buscar_Registro(ByVal xId As Integer) As Data
....
If (Ds.Tables(0).Rows.Count = 0) Then
Return Nothing
Else
a = Ds.Tables(0).Rows(0)("Nombre").ToString()
b = Ds.Tables(0).Rows(0)("Apellido").ToString()
Ds.Dispose()
return new Data() With {.Nombre = a, .Apellido = b}
End If
End Function
As of VB 15 you can use ValueTuple
Function Buscar_Registro(ByVal xId As Integer) As (Nombre As String, Apellido As String)
....
If (Ds.Tables(0).Rows.Count = 0) Then
Return (Nothing, Nothing)
Else
a = Ds.Tables(0).Rows(0)("Nombre").ToString()
b = Ds.Tables(0).Rows(0)("Apellido").ToString()
Ds.Dispose()
Return (a, b)
End If
End Function
I'm new to .net but wouldn't "ByRef" be the easiest way?
Function Buscar_Registro(ByVal xId As Integer, ByRef a As String, ByRef b As String)
why I only get "0" back? My Code:
Dim GeldAdresse As String = "811768"
Dim offsets(1) As Integer
offsets = {&H28, &H2C}
Dim ergebniss As Integer = Memory.ReadPointerInteger("eurotrucks2", 811768, offsets)
The Module "Memory":
Public Function ReadPointerInteger(ByVal EXENAME As String, ByVal Pointer As Integer, ByVal ParamArray Offset As Integer()) As Integer
Dim Value As Integer
If Process.GetProcessesByName(EXENAME).Length <> 0 Then
Dim Handle As Integer = Process.GetProcessesByName(EXENAME)(0).Handle
If Handle <> 0 Then
For Each I As Integer In Offset
ReadMemoryInteger(Handle, Pointer, Pointer)
Pointer += I
Next
ReadMemoryInteger(Handle, Pointer, Value)
End If
End If
Return Value
End Function
In the Example:
' Me.Text = ReadPointerInteger("gta_sa", &HB71A38,&H540,&H544).ToString()
Must I convert 0x2C in a Integer?
Dim Value As Integer
...
Return Value
You are returning Value but never assing a real value to it. Looks like you need to change your code to:
Dim Value As Integer
...
Value = ReadMemoryInteger(...
...
Return Value
The class is broken. I use antoher.
This question is a follow-on to VB ReDim of member field programmatically. After the arrays are dimensioned appropriately, I try to set the values of the elements, but I get an exception at run time when I try to assign the first value (MySB.AssignValues(0, "B", 0, 7.6))
System.InvalidCastException was unhandled
HResult=-2147467262
Message=Object cannot be stored in an array of this type.
Source=mscorlib
Module TestSetArray
Public Class BS
Public A As String
Public B() As Double
Public C() As Double
End Class
Public Class SB
Public MyBS() As BS
'ReadFieldString is a function that returns a string of the field name of Class BS,
'i.e., A, B or C. For test purpose, retun a constant
Public Function ReadFieldString() As String
Return "B"
End Function
'GetArrayDim is a function that returns an integer, which is the size of the array
'of that field name. For test purpose, retun a constant
Public Function GetArrayDim() As Integer
Return 2
End Function
Public Sub DimArrays()
ReDim MyBS(3)
Dim i As Integer
For i = 0 To MyBS.Length - 1
MyBS(i) = New BS()
Dim f = GetType(BS).GetField(ReadFieldString())
f.SetValue(MyBS(i), Array.CreateInstance(f.FieldType.GetElementType(), GetArrayDim()))
Next
End Sub
Public Sub AssignValues(MainIndex As Integer, TheName As String, TheIndex As Integer, TheValue As Double)
Dim f = MyBS(MainIndex).GetType.GetMember(TheName)
f.SetValue(TheValue, TheIndex)
End Sub
End Class
Sub Main()
Dim MySB As SB = New SB
MySB.DimArrays()
MySB.AssignValues(0, "B", 0, 7.6)
MySB.AssignValues(0, "B", 1, 8.2)
End Sub
End Module
Thanks in advance.
The problem is that the GetMember method returns an array of type MemberInfo, not the double array of the class. You'd probably have an easier time if you used GetField instead. You have to call GetValue and cast its result to an Array in order to use SetValue to set the value.
Public Sub AssignValues(MainIndex As Integer, TheName As String, TheIndex As Integer, TheValue As Double)
Dim f = MyBS(MainIndex).GetType().GetField(TheName)
Dim doubleArray = DirectCast(f.GetValue(MyBS(MainIndex)), Array)
doubleArray.SetValue(TheValue, TheIndex)
End Sub
or if you know that the array will always be an array of Double, you can cast it directly to that:
Public Sub AssignValues(MainIndex As Integer, TheName As String, TheIndex As Integer, TheValue As Double)
Dim f = MyBS(MainIndex).GetType().GetField(TheName)
Dim doubleArray = DirectCast(f.GetValue(MyBS(MainIndex)), Double())
doubleArray(TheIndex) = TheValue
End Sub