Return values with reference arguments or by structure - vb.net

In what situtations is it best to use reference arguments to return values?
Sub Example(byref value as integer)
value = 3
End Sub
In what situations is it best to return the value (perhaps in a structure for more complex types)?
Function Example() as integer
return 3
End Function

In general, I'd avoid using reference arguments to return values.
THe design guidelines suggest avoiding this, which is why the Microsoft code analysis tools warn you when they find it.Do not pass types by reference.
It's nearly always more maintainable to return values instead of passing arguments by reference, unless there is a very specific need to do so. If you're generating a new value, return it.

when you want to return a state or status of an operation plus the result from the operation.
think of TryParse..it returns a conversion result as true or false and it returns the converted value by a ref variable.
Dim number As Integer
Dim result As Boolean = Int32.TryParse(value, number)
Public Shared Function TryParse ( _
s As String, _
<OutAttribute> ByRef result As Integer _
) As Boolean
but other than that, as others suggested i would not use by ref a lot, it can make code very hard to read and debug.

It really depends what the function's doing. Generally, though, if there's only one return, by value is easier for the caller. They can simply do:
int foo = Example(foo)
or:
int modifiedFoo = Example(foo)
as they prefer.

Related

VB.Net - Can you access the expected data type within a function?

I was wondering if there is any way to access the expected data type within a function similar to an event arg. I am doubtful that this is possible, though it would be an excellent feature.
I frequently work with (old and disorganized)Mysql databases creating interfaces through VB.Net. Often I will have an optional field which contains a NULL value in the database. I am frequently dealing with errors due to NULL and dbnull values in passing data to and from the database.
To complicate things, I often am dealing with unexpected datatypes. I might have an integer zero, a double zero, an empty string, or a string zero.
So I spend a fair amount of code checking that each entry is of the expected type and or converting NULLs to zeros or empty strings depending on the case. I have written a function ncc(null catch convert) to speed up this process.
Public Function ncc(obj As Object, tp As Type) As Object 'Null Catch Convert Function...
My function works great, but I have to manually set the type every time I call the function. It would be so much easier if it were possible to access the expected type of the expression. Here is an example of what I mean.
Dim table as datatable
adapter.fill(table)
dim strinfo as string
dim intinfo as long
strinfo = ncc(table.Rows(0).Item(0),gettype(String)) 'here a string is expected
intinfo = ncc(table.Rows(0).Item(0),gettype(Long)) 'here a long is expected
It would be so much more efficient if it were possible to access the expected type directly from the function.
Something like this would be great:
Public Function ncc(obj As Object, optional tp As Type = nothing) As Object
If tp Is Nothing Then tp = gettype(ncc.expectedtype)
That way I do not have to hard code the type on each line.
strinfo = ncc(table.Rows(0).Item(0))
You can make the ncc function generic to simplify calling it:
Public Function ncc(Of T)(obj As T) As T
If DbNull.Value.Equals(obj) Then Return Nothing
Return Obj
End Function
This kind of function will be able to in some cases infer the type, but if there's any possibility of null you'll still want to include a type name (because DBNull will be the inferred type for those values). The advantage is not needing to call gettype() and so gaining a small degree of type safety:
strinfo = ncc(Of String)(table.Rows(0).Item(0))
But I think this has a small chance to blow up at run time if your argument is not implicitly convertible to the desired type. What you should be doing is adding functions to accept a full row and return a composed type. These functions can exist as static/shared members of the target type:
Shared Function FromDataRow(IDataRow row) As MyObject
And you call it for each row like this:
Dim record As MyObject = MyObject.FromDataRow(table.Rows(i))
But, you problem still exists.
What happens if the column in the database row is null?
then you DO NOT get a data type!
Worse yet? Assume the data column is null, do you want to return null into that variable anyway?
Why not specify a value FOR WHEN its null.
You can use "gettype" on the passed value, but if the data base column is null, then you can't determine the type, and you right back to having to type out the type you want as the 2nd parameter.
You could however, adopt a nz() function (like in VBA/Access).
So, this might be better:
Public Function ncc(obj As Object, Optional nullv As Object = Nothing) As Object
If obj Is Nothing OrElse IsDBNull(obj) Then
Return nullv
End If
Return obj
End Function
So, I don't care if the database column is null, or a number, for such numbers, I want 0.
So
dim MyInt as integer
Dim MyDouble As Double
MyInt = ncc(rstData.Rows(0).Item("ContactID"), 0)
MyDouble = ncc(rstData.Rows(0).Item("ContactID"), 0)
dim strAddress as string = ""
strAddress = ncc(rstData.Rows(0).Item("Address"), "")
Since in NEAR ALL cases, you need to deal with the null from the DB, then above not only works for all data types, but also gets you on the fly conversion.
I mean, you CAN declare variables such as integer to allow null values.
eg:
dim myIntValue as integer?
But, I not sure above would create more problems than it solves.
So,
You can't get exactly what you want, because a function never has knowledge of how it's going to be used. It's not guaranteed that it will be on the right-hand side of an assignment statement.
If you want to have knowledge of both sides, you either need to be assigning to a custom type (so that you can overload the assignment operator) or you need to use a Sub instead of an assignment.
You could do something like this (untested):
Public Sub Assign(Of T)(ByVal field As Object, ByRef destination As T,
Optional ByVal nullDefault As T = Nothing)
If TypeOf field Is DBNull Then
destination = nullDefault
Else
destination = CType(field, T)
End If
End Sub
I haven't tested this, so I'm not completely certain that the compiler would allow the conversion, but I think it would because field is type Object. Note that this would yield a runtime error if field is not convertible to T.
You could even consider putting on a constraint requiring T to be a value type, though I don't think that would be likely to work because you probably need to handling String which is a reference type (even though it basically acts like a value type).
Because the destination is an argument, you wouldn't ever need to specify the generic type argument, it would be inferred.

Different ways of using a variable across different subroutines

I'm trying to set up a sub to be called upon and use the value of its result in the main sub. So far I've been using Function to carry over the value. However, I was wondering if there are any alternative ways of doing the same thing? I figured ByVal/ByRef is another way to do it by using a Sub instead of Function. My current codes are as follow:
Sub Main()
Dim i as Long
i = lr("A")
'some other calculations using i
End Sub
Function lr(Tar As String) As Long
Dim twb As Workbook
Set twb = ThisWorkbook
lr = ThisWorkbook.Sheets(1).Range(Tar & Rows.Count).End(xlUp).Row
End Function
My question is, How would I write this if I were to use a Sub instead of Function? Thanks!
So far I've been using Function to carry over the value.
Great, that's what functions are for! When you only need to return a single value, the best way is always going to be a function.
Things get fuzzier when you start needing to return two or more values. You could:
Use ByRef parameters and use them as "out" values.
This is "ok" for procedures (Sub), and confusing for functions - what determines which parameter gets to be the function's return value, and which parameters get to be passed ByRef? How does the calling code know whether or not to pass an initialized value to those ByRef parameters?
A naming convention can help here:
Public Sub Foo(ByVal foo1 As String, ByRef outBar1 As String, ByRef outBar2 As String)
An "out" prefix can tell the calling code that the parameter is an out value.
Scope the variables at a level that is accessible by both the caller and the callee.
This is a bad practice that can easily lead to spaghetti code. Avoid it - variables should have the smallest necessary scope possible, and be passed between methods/functions/procedures/modules, not just globally scoped and accessed by anyone at any given time!
Create a class to encapsulate all the values the function should return.
Definitely more object-oriented, results in much cleaner, readable code. The only downside is that VBA doesn't really encourage you to do this, and consistently doing that will result in a myriad of classes that you can't quite organize.

Function in VB that doesn't have a return type

I am not much familiar with Visual Basic 6.0 and am not having a VB compiler installed, but I was looking at some VB code for some debugging and saw this:
Private Function IsFieldDeleted(oLayoutField As Object)
Dim oColl As Collection
Set oColl = GetFieldIdsForField(oLayoutField)
IsFieldDeleted = (oColl.Count = 0)
Set oColl = Nothing
End Function
In other functions I see they define the return type with an "As" for example "As Boolean" but this one does not have an "As" :D and then how they have used it is like this:
If Not IsFieldDeleted(oRptField.GetUCMRLayoutField) Then
Call oCollection.Add(oRptField, oRptField.ObjectKeyString)
Call AddToNewLineSeperatedString(sCaseFldDescMsg, oFld.FieldDescription)
End If
How is this working? Is it just like rewriting it and saying that the function returns an integer and compare the return type to be either 0 or 1? Or are there some other hidden tips in there?
When no type is specified, in VB.NET it assumes Object for the return type. In VB6, it assumes Variant. In VB.NET you can make things much more obvious by turning Option Strict On, but I don't believe that option was available in VB6.
The value that is returned, in reality, is still typed as a Boolean, but you are viewing the returned value as a Variant. So, to do it "properly", you really ought to cast the return value like this:
If Not CBool(IsFieldDeleted(oRptField.GetUCMRLayoutField)) Then
....
End If
Calling CBool casts the value to a Boolean instead of a Variant. This is unnecessary, though, since VB will use late-binding to determine the type of the return value is a boolean.
The best thing to do in this case is to change the function to As Boolean. Doing so will not break any existing code since that's all it ever returned anyway. However, if it's a public member in a DLL, that would break compatibility.

Looping Through Boolean Parameters

I have a generic question to see if anyone can help me with a better solution.
I have a .NET method that takes in 20+ boolean values, passed in individually.
For each parameter that is true I need to add a value to list.
Is there a more efficient way to add the values to the list besides have an if statement for each boolean?
Example:
Public Function Example(ByVal pblnBool1 as boolean, _
ByVal pblnBool2 as boolean, _
ByVal pblnBool3 as boolean)
If pblnBool1 then
list += "A"
End If
If pblnBool2 then
list += "B"
End If
End Function
Obviously this code isn't correct but it shows what I'm trying to do.
Anyone have any ideas?
Thanks
First off, having 20+ params sucks.
Secondly, you can use the ParamArray keyword to declare that you want the values passed to you in an array. (I don't think this is CLS compliant, meaning some languages won't be able to call your function without bundling the values into an array. But VB and C# can both easily work with each other's param arrays.) If you don't want to do that, you can always create the array yourself in your function. But i'd rather let the language and/or framework do that for me.
This is not optimized or anything; it's just an example.
sub Example(paramarray bools() as Boolean)
static vals() as String = {"A", "B", "C"}
if bools.Length > vals.Length then
throw new ArgumentException(String.Format( _
"Too many params! ({0} max, {1} passed)", _
vals.Length, bools.Length _
))
end if
for i as Integer = 0 to bools.Length - 1
if bools(i) then list += vals(i)
next
end sub
I assume that list is some member variable, since it's not defined in your code. If you intend for it to be the return value, then declare it in the function, and return it at the end. (And of course, turn sub Example(...) and end sub into function Example(...) as String and end function).
If you have 32 or fewer booleans to pass, you can use an instance of BitVector32. This allows to pass them all in a single integer. It provides methods for setting and retrieving the values.
Just make an array of boolean values and do a for...each loop through it. Or if you need to be selective, a 2-D array.

VB.NET: Assign value to variable inside an IF condition?

is there any possibility to assign a value to a variable inside an IF condition in VB.NET?
Something like that:
Dim customer As Customer = Nothing
If IsNothing(customer = GetCustomer(id)) Then
Return False
End If
Thank you
Sorry, no. On the other hand, it would get really confusing, since VB.NET uses the same operator for both assignment and equality.
If a = b Then 'wait, is this an assignment or comparison?!
Instead, just set the variable and compare:
Dim customer As Customer = Nothing
customer = GetCustomer(id)
If IsNothing(customer) Then
Return False
End If
No, I'm fairly sure this is not possible - but be thankful!
This is a "feature" of C-based languages that I would never encourage the use of, because it is probably misused or misinterpreted more often than not.
I think the best approach is to try and express "one thought per line", and resist coding that combines two operations in the same line. Combining an assignment and a comparison in this way usually doesn't achieve much other than making the code more difficult to understand.
VB doesn't do that very well, especially since assignment and comparison both use the = operator. It'd get really confusing. And since VB to some degree is designed to be newbie-friendly (The B in "BASIC" actually stands for "Beginner's"), expressions with multiple side effects are not something the language generally likes to do.
If it's essential that you be able to do it all in one line, then you could make the GetCustomer method assign to a ByRef parameter, and return a boolean saying whether the assigned value was null. But really, it's better to just assign and then compare to null.
There is no builtin support for doing this, but you could use this workaround.
Public Function Assign(Of T)(ByRef destination As T, ByVal value As T) As T
destination = value
Return destination
End Function
And then it could be used like this.
Dim customer As Customer = Nothing
If IsNothing(Assign(customer, GetCustomer(id))) Then
Return False
End If
Thinking out loud because you didn't show Customer or GetCustomer...
Dim myCust As New Customer
If myCust.GetCustomer(someID) Then
End If
Class Customer
Private _customerID As Integer
Const noCustomer As Integer = -1
Public Sub New()
Me._customerID = noCustomer 'no customer
End Sub
Public Function GetCustomer(ByVal id As Integer) As Boolean
'perform required action to get customer
'if successful then set Me._customerID to ID else set it to no customer value
Return Me.HaveCustomer
End Function
Public Function HaveCustomer() As Boolean
If Me._customerID = noCustomer Then Return False Else Return True
End Function
End Class
Yes, it is possible to assign a value to a variable inside an IF condition in VB.NET.
There is even builtin support for doing this, albeit to a limited extend:
If Interlocked.Exchange(customer, GetCustomer(id)) Is Nothing Then
Return False
End If
where the methods within the Interlocked class are intended to be used within a multi-threading environment. By design, the return value of Exchange() is the old value, rather than the new one. Thus, to obtain the rather cryptic equivalent result:
If (customer = Interlocked.Exchange(customer, GetCustomer(id)) And customer Is Nothing Then
return false
End If
All of the people stating that this is an issue as VB uses the "same operator for both assignment and equality"..
I would argue that this is not a good reason as they could simply make slightly different syntax for it... for example:
Dim[Name = Expression]
If a = Dim[qwe = 1 + 2] Then
'qwe = 3
End If
'...
a = 3
If Dim[qwe = a = 1 + 2] Then
'qwe = True
End If