Recently I was going through several small exercises in development. In one of them I was asked to convert a string to a number in VB .NET respecting the current locale. So I did :
Imports System
Public Module Aplicacion
Public Sub Main()
Console.WriteLine(Numero("Hello World"))
Console.WriteLine(Numero(2))
Console.WriteLine(Numero(.24))
Console.WriteLine(Numero(2.4))
End Sub
Function Numero(input as String) AS Double
Try
return Convert.ToDouble(input)
Catch Ex AS Exception
return 0
End Try
End Function
End Module
The teacher corrected this by telling me that "although it works, the right way is to declare a variable with the same type I am going to return, calculate its value in the body of the function and at the end return this variable".
Is there really any gain in doing that against doing it directly as I did?
Maybe some compilation bonus or an improvement in performance?
Function Numero(input As Object)
If input.GetType Is GetType(String) Then
Return 0
Else
Return input
End If
End Function
Sub Main()
Console.WriteLine(Numero("$$$$325FK-.,ASL$$$"))
Console.WriteLine(Numero(1))
Console.WriteLine(Numero(1.5))
Console.ReadKey()
End Sub
Related
Consider :
Private Function isAvailableQuantity() As Boolean
Try
sqL = "SELECT StocksOnHand FROM ITEM WHERE ItemNo = " & Val(txtSearch.Text) & ""
ConnDB()
cmd = New OleDbCommand(sqL, conn)
dr = cmd.ExecuteReader(CommandBehavior.CloseConnection)
If dr.Read = True Then
If Val(txtQuantity.Text) <= dr(0) Then
isAvailableQuantity = True
Else
MsgBox("Insuficient stocks", MsgBoxStyle.Critical, "Validate Stocks")
txtSearch.Clear()
End If
End If
Catch ex As Exception
MsgBox(ex.Message)
Finally
cmd.Dispose()
conn.Close()
End Try
End Function
I don't know what to do. The older version visual studio doesn't get this error.
i am using vs 2022 right now and it seems to have an error on the contrary vs 2010 doesn't
In VB.NET, a Function is a method that returns a value and a Sub is a method that doesn't return a value. If your method doesn't need to return anything, use a Sub, e.g.
Private Sub DoSomething()
'Do some stuff here.
End Sub
If you do use a Function then there are two ways to return a value. The bad way is to assign a value to the implicit local variable that is named after the method, e.g.
Private Function DoSomething() As Boolean
'Do some stuff here.
DoSomething = True
End Function
That way basically only exists to support upgraded VB6 code. If your teacher is showing you that then they are obviously an old VB6 developer who hasn't actually learned VB.NET properly. The good way to return a value is with an explicit Return statement, e.g.
Private Function DoSomething() As Boolean
'Do some stuff here.
Return True
End Function
If there are multiple paths that execution could take through your code, you need to make sure that a value is returned on all of them. As an example, this code does not do that:
Private Function DoSomething() As Boolean
If Date.Now.DayOfWeek = DayOfWeek.Monday Then
Return True
End If
End Function
That will return True on Mondays but it doesn't return anything on other days. In this specific case, one fix would be this:
Private Function DoSomething() As Boolean
If Date.Now.DayOfWeek = DayOfWeek.Monday Then
Return True
End If
Return False
End Function
If you are going to use the bad way to return a value and you want to return a particular value on all but one code path then the logical thing to do is to set the return value at the start to the default and then only change it in that one place, e.g.
Private Function DoSomething() As Boolean
DoSomething = False
If Date.Now.DayOfWeek = DayOfWeek.Monday Then
DoSomething = True
End If
End Function
That last example is the easiest (although not best) fix for your scenario.
I'm writing a project where this will be extended with further modules later on, and as such i want as much of the code as possible to be independent, so I'm using various methods to keep everything as generic as possible, and not require hard-coding lists of types, classes etc.
As a Proof of concept, I've come up with the following code in a class;
Public Class clsAddition
Public Function DoAction(Value1 As String, Value2 As String) As String
Dim dblTmpRet As Double
Dim strTmpRet As String = ""
If IsNumeric(Value1) = False Then strTmpRet += "Argument 'Value1' is not numeric. "
If IsNumeric(Value2) = False Then strTmpRet += "Argument 'Value2' is not numeric. "
If strTmpRet = "" Then
dblTmpRet = CDbl(Value1) + CDbl(Value2)
strTmpRet = CStr(dblTmpRet)
End If
Return strTmpRet
End Function
Private Function IsNumeric(Input As String) As Boolean
Dim blnTmpRet As Boolean
Try
Dim dblTest As Double = CDbl(Input)
blnTmpRet = True
Catch ex As Exception
blnTmpRet = False
End Try
Return blnTmpRet
End Function
Public Sub New()
MyBase.New()
End Sub
End Class
And the following code in a windows form;
Private objObjectClass As Object
Private Sub cmdCreateObject_Click(sender As Object, e As EventArgs) Handles cmdCreateObject.Click
Dim assem As Assembly = GetType(Form1).Assembly
Dim typChosen As Type
Try
typChosen = assem.GetType(Me.comObjectType.SelectedItem.ToString, True)
Catch ex As Exception
typChosen = Nothing
End Try
If Not IsNothing(typChosen) Then
'found it
objObjectClass = Activator.CreateInstance(typChosen, True)()
Else
'didn't find it
Throw New Exception("Unable to locate type in assembly")
End If
End Sub
The issue is, that on the "Activator.CreateInstance(typChosen, True)()" line, the following error is thrown
An unhandled exception of type 'System.MissingMemberException'
occurred in Microsoft.VisualBasic.dll Additional information: No
default member found for type 'clsAddition'
The aim is for me to be able to create instances of classes, then call a known name function on them. In this example, I'd create another class called "Divide", or another one that was called "Concatinate" etc, however in the real program these classes will all perform very different actions, have different properties, methods etc, with this one function ("DoAction" in the above example) sharing a syntax and return type.
Please help! been banging my head on this for a while now!
TIA!
As Per Hans Passant comment, this is due to the extra parenthesis on the following line;
Activator.CreateInstance(typChosen, True)()
Changing this to ;
Activator.CreateInstance(typChosen, True)
resolves the issue.
Currently I am receiving the error unable to cast table to IEnumerable, which I am finding very perplexing as at no time am I specifying a table.
I will explain step by step what is being done and please do point out where I went wrong.
DAL:
Public Function GetCategories(ByVal DB As WholeSaleDataDataContext, Optional CategoryID? As Integer = Nothing) As IEnumerable(Of Category)
If CategoryID Is Nothing Then
Return DB.Categories.AsEnumerable()
Else
Return DB.Categories.Where(Function(Cat) Cat.CategoryID.Equals(CategoryID)).AsEnumerable
End If
End Function
WCF:
<DataContract(), KnownType(GetType(WholeSalesDAL.Category))>
Public Class Category2
End Class
Public Function GetCategory() As IEnumerable(Of Category2) Implements IWholeSaleData.GetCategory
Return WholeSalesDAL.GetCategories(_DB).AsEnumerable
End Function
Website:
Private WholeSaleServices As WholeSaleService.WholeSaleDataClient
Public Sub New()
WholeSaleServices = New WholeSaleService.WholeSaleDataClient("BasicHttpBinding_IWholeSaleData", "http://localhost:16722/WholeSaleService.svc")
GetCategories()
End Sub
Public Sub GetCategories()
Dim z = WholeSaleServices.GetCategory
End Sub
I see two problems with your code:
EDIT: #1 explained...
1) In your WCF section, WholeSalesDAL.GetCategories(_DB) is IEnumerable(Of Category), but your function is supposed to return `IEnumerable(Of Category2). I don't see how that type conversion is being done.
2) I believe you are using AsEnumerable unnecessarily, and that is probably what is causing the exception. Do you know if you need to call AsEnumerable in either spot you have it? I would try removing it.
I am using VB.Net 4 and am successfully using a class with the following signature:
Sub Register(Of TMessage)(recipient As Object, action As
System.Action(Of TMessage))
I want to learn about lambdas in VB so I wanted to see if I can simplify my current code.
CURRENTLY: I have the following in the constructor of a class (simplified for clarity)
Public Sub New()
Dim myAction As New Action(Of String)(AddressOf HandleMessage)
Messenger.Default.Register(Of [String])(Me, myAction)
End Sub
And later in the class I have the following:
Private Function HandleMessage(sMsg As String)
If sMsg = "String Value" Then
If Me._Selection.HasChanges Or Me._Selection.HasErrors Then
Return True
End If
Return False
Else
Return False
End If
End Function
QUESTION: Is there a way to simplify this into a lambda like in C# where I don't have to declare the MyAction variable in the constructor, but just pass the string value to HandleMessage function "inline" with the register sub? (I hope that makes sense)
So your constructor code is equivalent to:
Messenger.[Default].Register(Of String)(Me,
Function(sMsg)
If sMsg = "String Value" Then
If Me._Selection.HasChanges Or Me._Selection.HasErrors Then
Return True
End If
Return False
Else
Return False
End If
End Function)
It's worth mentioning though you don't need to use lambdas just to get rid of your explicit delegate creation. This is perfectly legal too:
Messenger.[Default].Register(Of String)(Me, AddressOf HandleMessage)
There is a bit of strangeness though with your example: you've declared your Register method as taking an Action(Of TMessage) which means the function passed needs no return value. Your function however is returning a Boolean. Visual Basic here is doing the fancy "delegate relaxation" and is throwing away the return value. So all your Return True/Return False bits aren't actually doing anything special.
Microsoft (and many developers) claim that the SqlDataReader.GetOrdinal method improves the performance of retrieving values from a DataReader versus using named lookups ie. reader["ColumnName"]. The question is what is the true performance difference if dealing with small, paged record sets? Is it worth the extra overhead of finding and referencing ordinal indexes throughout the code?
Microsoft recommends not calling GetOrdinal within a loop.
That would include indirect calls with the string indexer.
You can use GetOrdinal at the top of your loop put the ordinals in an array and have the indexes in the array be const or have an enum for them (no GetOrdinal at all) or use GetOrdinal into individual variables with descriptive names.
Only if your sets are small would I really consider this to be premature optimization.
It's apparently a 3% penalty.
Any difference will be more than outweighed by maintenance overhead.
If you have that much data that it makes a noticeable difference, I'd suggest you have too much data in your client code. Or this is when you consider use ordinals rather than names
Yes and no.
If you're dealing with a massive amount of data then you'd certainly benefit from using the ordinals rather than the column names.
Otherwise, keep it simple, readable, and somewhat safer - and stick with the column names.
Optimize only when you need to.
I created a wrapper for SqlDataReader that stores orindals in a dictionary with the column name as the key.
It gives me ordinal performance gains while keeping the code more readable and less likely to break if someone changes the column order returned from stored procedures.
Friend Class DataReader
Implements IDisposable
Private _reader As SqlDataReader
Private _oridinals As Dictionary(Of String, Integer)
Private Shared _stringComparer As StringComparer = StringComparer.OrdinalIgnoreCase 'Case in-sensitive
Public Sub New(reader As SqlDataReader)
Me._reader = reader
Me.SetOrdinals()
End Sub
Private Sub SetOrdinals()
Me._oridinals = New Dictionary(Of String, Integer)(_stringComparer)
For i As Integer = 0 To Me._reader.FieldCount - 1
Me._oridinals.Add(Me._reader.GetName(i), i)
Next
End Sub
Public Function Read() As Boolean
Return Me._reader.Read()
End Function
Public Function NextResult() As Boolean
Dim value = Me._reader.NextResult()
If value Then
Me.SetOrdinals()
End If
Return value
End Function
Default Public ReadOnly Property Item(name As String) As Object
Get
Return Me._reader(Me.GetOrdinal(name))
End Get
End Property
Public Function GetOrdinal(name As String) As Integer
Return Me._oridinals.Item(name)
End Function
Public Function GetInteger(name As String) As Integer
Return Me._reader.GetInt32(Me.GetOrdinal(name))
End Function
Public Function GetString(ordinal As Integer) As String
Return Me._reader.GetString(ordinal)
End Function
Public Function GetString(name As String) As String
Return Me._reader.GetString(Me.GetOrdinal(name))
End Function
Public Function GetDate(name As String) As Date
Return Me._reader.GetDateTime(Me.GetOrdinal(name))
End Function
Public Function GetDateNullable(name As String) As Nullable(Of Date)
Dim o = Me._reader.GetValue(Me.GetOrdinal(name))
If o Is System.DBNull.Value Then
Return Nothing
Else
Return CDate(o)
End If
End Function
Public Function GetDecimal(name As String) As Decimal
Return Me._reader.GetDecimal(Me.GetOrdinal(name))
End Function
Public Function GetBoolean(name As String) As Boolean
Return Me._reader.GetBoolean(Me.GetOrdinal(name))
End Function
Public Function GetByteArray(name As String) As Byte()
Return CType(Me._reader.GetValue(Me.GetOrdinal(name)), Byte())
End Function
Public Function GetBooleanFromYesNo(name As String) As Boolean
Return Me._reader.GetString(Me.GetOrdinal(name)) = "Y"
End Function
'Disposable Code
End Class