What is the difference between Sub and Function in VB6? - vb6

I am going through some old VB code and I run into function definitions like these -
Private Function ExistingCustomer(Index As Integer, Customer As String) As Integer
Private Sub cmdCustomerList_Click()
What's the difference?

Function returns value, Sub doesn't. It's that simple.

A function can also be used in an expression. A Subroutine cannot.
Functions can lend to the readability of your code better than a subroutine.
Here's an example of how a function can increase readability:
If AccountIsLocked("JJones") then Msgbox("This account is locked")
this function would be defined somewhere
public function AccountIsLocked(UserId as string) as boolean
dim usr = uow.AccountRepository.UserInfo(UserId)
return usr.locked
end function
Here's the same example but coded with a subroutine:
CheckIfAccountLocked("JJones")
and elsewhere this sub is defined:
public sub CheckIfAccountLocked(UserId)
if uow.AccountRepository.UserInfo(UserId).locked then
msgbox("Account is locked")
end if
end sub
Also note that checking the value is separated from the action -- this contributes to separation of duties. The function would lend toward re-usability.
With VB6 there are some odd rules governing parenthesis. If there are no parameters to a sub then the parenthesis are not needed (I think Visual Studio might remove the parenthesis). One way around this is to add the keyword "Call" before your sub.
Call CheckIfAccountLocked()
vs
CheckIfAccountLocked

In function we can return values as boolean, string and other data data types.
but sub does not return any thing.
it just executes code and instruction that we give. These are also regarded as methods
Sub is also used in control's events and these events also not return any value.
for example the click event of a command button:
Private sub cmd_click()
end sub

They are both sections to write code however a function must return a value. For example if you had a program in which a complicated mathematical procedure needs to be executed a number of times you would simply make a function and have the complicated maths code in there and any time you need to do the calculation you can just call the function. Hope this helped not sure if I explained it well.

What is the difference between Sub and Function in VB6?
"sub" can perform some action.
"sub" returns no value.
Example:
Form_Load()
"function" can also perform some action but it also returns some value to point from which it was called.
that is, "Functions return a value, often based on a variable"
Example:
Val(), FormatPercentage().

function in vb
a function must return some value/s
Syntax :
private function fun_name(argument/s(optional)) as return_type(integer,string..)
return value
end function
fun_name(arguments(optional) ) is enough for function call
sub in vb
a sub need not to be return any value/s
Syntax :
private sub sub_name(argument/s(optional))
end sub
sub_name(arguments(optional) ) is enough for function call

A function holds data and code. But a subroutine contains only code, but not data.

Syntax of functions will be Function...End function and for Sub will be Sub...End Sub.
Functions may or may not have objects but sub doesn't have objects
Functions are re-usable where Sub doesn't
Functions can return values but sub doesn't
Functions may have object repository but sub doesn't
Extension of functions is .qfl where for sub it's .vba

Related

Late binding with COM objects (flexgrid) is 2 times slower than early binding

Public Function gridloop(MSFG1 As Object) As Long
For i= 0 To MSFG1.rows - 1
A = MSFG1.TextMatrix(i,1)
Next
End Function
The above code is 2 times slower than below
Public Function gridloop(MSFG1 As MSHFlexGrid) As Long
Public Function gridloop(MSFG1 As MSFlexGrid) As Long
Any solution to speed-up?
Not a lot of details in the question, I presume you have two (or more?) different controls where you're trying to essentially overload your gridloop function so it'll work with multiple types of controls?
The following might provide a performance improvement. I have not tested this, not even confirmed that it is free of compile errors. Idea is to determine the control type, then assign it to a variable of a matching type, then the references to the methods and properties might be early bound (thus faster).
Public Function gridloop(MSFG1 as Object) as Long
Dim myMSHFlexGrid As MSHFlexGrid
Dim myMSFlexGrid As MSFlexGrid
Dim i As Integer
Dim A As Long
If TypeOf MSFG1 Is MSHFlexGrid Then
Set myMSHFlexGrid = MSFG1
For i = 0 To myMSHFlexGrid.rows - 1
A = myMSHFlexGrid.TextMatrix(i,1)
Next
ElseIf TypeOf MSFG1 Is MSFlexGrid Then
Set myMSFlexGrid = MSFG1
For i = 0 To myMSFlexGrid.rows - 1
A = myMSFlexGrid.TextMatrix(i,1)
Next
End If
End Function
Alternative is to define two gridloop functions, one for each type. A form of manual overloading.
Public Function gridloop_MSHFlexGrid(MSFG1 As MSHFlexGrid) As Long
Public Function gridloop_MSFlexGrid(MSFG1 As MSFlexGrid) As Long
Advantage to this is that trying to call one of the gridloop functions with an 'incorrect' control will result in a compile error - catching a problem early that could otherwise require spending some significant time performing runtime debugging.
Building on MarkL's answer, you could use actual VB interface overloading to get what you want.
The idea would be to create an interface exposing whatever properties or functions you need on the grids, and then create two classes, each one implementing that interface and internally manipulating the actual grid.
This wrappering substitutes for the fact that the two grid types do not intrinsically share a common interface. (I looked in the IDL using OLEView).
You can then use the interface as the type in every location you currently are using Object to stand in for the actual grid class. If the interface is comprehensive AND its methods / properties are named appropriately then you would not need to make any other code changes.
Sample (pseudo)code...
Interface:
'In file for interface IGridWrapper
Function Rows
End Function
Function TextMatrix(i as Integer, j as Integer)
End Function
'Any others...
Wrapper class 1:
' In file for class "MSFlexGridWrapper"
Implements IGridWrapper
Private m_grid as MSFlexGrid
Sub Init(MSFlexGrid grid)
Set m_grid = grid
End Sub
Function IGridWrapper_Rows
IGridWrapper_RowCount = m_grid.Count
End Function
Function IGridWrapper_Textmatrix(i as Integer, j as Integer)
'etc.
End Function
'Any others...
Wrapper class 2:
' In file for class "MSHFlexGridWrapper"
Implements IGridWrapper
Private m_grid as MSHFlexGrid
Sub Init(MSHFlexGrid grid)
Set m_grid = grid
End Sub
Function IGridWrapper_Rows
IGridWrapper_RowCount = m_grid.Count
End Function
Function IGridWrapper_Textmatrix(i as Integer, j as Integer)
'etc.
End Function
'Any others...
Code using the wrappers:
Public Function gridloop(MSFG1 As IGridWrapper) As Long
(Note - none of this has been put through a compiler for exact syntax checking)
The basic reason that late binding (binds at runtime) is slower than early binding (binds at compile time) is that you have to use the iDispatch interface's GetIDsOfNames and Invoke methods to access properties in the object's interface, rather than accessing them directly from the vtable. For more information, have a look at this.
The reason that DaveInCaz's and MarkL's suggestions will probably speed things up is that they are ways to allow your gridloop function to accept a type that can be bound early rather than an Object type. DaveInCaz's solution is also a fine example of a practical application of polymorphism.

"That name is not valid" for my VBA function in Excel/Mac

A simple VBA function. When I try to use it in my worksheet, all I get, no matter what I do, is "That name is not valid". I'm out of ideas.
Sub FindABV(temperature)
With Worksheets("Sheet1")
.Range("C28").GoalSeek _
Goal:=temperature, _
ChangingCell:=.Range("B28")
End With
FindABV = .Range("B28").Value
End Sub
I've tried creating a new Module to put it in. No change.
And no error indications from the code editor.
The Sub procedure performs a task and then returns control to the calling code, but it does not return a value to the calling code.
See more here.
This said, you cannot set a procedure equal to something:
FindABV = .Range("B28").Value
because that name is not valid (you cannot say that a procedure is equal to a certain value, it doesn't make sense). You probably wanted to use a Function to return the value of that cell calculated by the Goal Seeker depending on the input temperature that you pass by the function:
Function FindABV(temperature)
With Worksheets("Sheet1")
.Range("C28").GoalSeek _
Goal:=temperature, _
ChangingCell:=.Range("B28")
End With
FindABV = .Range("B28").Value '<-- return the value
End Function
However, be careful: if =FindABV(temperature) lies on Sheet1.Range("B28"), you will have a circular reference because your function will try to have the value of itself.
Your code will not deliver the results you want. If you want to have the Function work for different values than the ones stored in B28 and C28 you'll have to write it more like this:
Public Function FindABV(goalCell As Range, changeCell As Range, temperature As Double)
goalCell.GoalSeek Goal:=temperature, ChangingCell:=changeCell
FindABV = changeCell
End Function
But this doesn't matter in any case because GoalSeek actually changes the value in the ChangingCell which Excel will not do if it is called from within a Function.

an IXMLDOMAttribute as return value of a VBScript function

I'm creating XML documents with VBScript and MSXML DOM. In order to structure and simplify my code I'm using classes and thus methods (functions in VBS) too.
Here is a little function that troubles me:
function createAttribute(name, value)
dim doc
Set doc = CreateObject("Msxml2.DOMDocument.4.0")
dim attr
set attr= doc.createNode(2,name,"")
attr.NodeValue=value
createAttribute=attr
end function
The assignment createAttribute=attr, where I'm setting the return value of the function, causes the following error:
Object doesn't support this property or method
As the web resources on XML processing with VBS are rather sparse, I hope some of you can help me to understand whats going on here. Here are my questions:
What object does not support what property or method?
Can i pass objects of any given class as return values of VBS functions?
Can i pass an object of the class IXMLDOMAttribute as a return value to a VBS function?
I think the problem is that attr is an object, so you need to use set to apply the return value. Otherwise, you may simply be returning attr's default property value (if it has one):
set createAttribute = attr
You don't show how you use the return value, so I can't comment on that but it is possibly the source of the error.

getting value from another sub in VB

I have this Sub which does the calculation get the information from CSV file.
Private Sub getTotalAmt(intDuration As integer, strProgrameType As String)
I've another Sub:
Private Sub getMembershipDiscount(ByRef dtDOB As Date, dblTotalAmt as Double)
which calculates the age of the customer and based on their age, gives them discount. So to do this, I've got to get the total amount from the above Sub (getTotalAmt) to this Sub procedure.
My question is, how to receive total amount from that sub procedure?
by definition, subroutines don't have output.
Instead, you want to write a function. In your case, change the code you have to something like this:
private function getTotalAmt(intDuration As integer, strProgrameType As String) as double
'do the same maths you do in the sub, and store your answer in a variable called "whatever"
getTotalAmt = whatever
end function
then to use the function:
sub IUseFunctions()
dim myDiscount as double
myDiscount = getTOtalAtm(1,"s")
end sub
In this example case, I've decided for you that the discount value is stored in a double type number. You can change that to whatever you like, at the top line of the getTotalAmt function.
Please speicify return types and convert your Sub to a Function
In that way you can always return something from your Functions,
if you specify a return type to your getTotalAmt(int Duration As Integer, StrProgrameType As String) method , That would be look like this Function getTotalAmt(int Duration As Integer, StrProgrameType As String) As double
then from another method you can call it and use retuned value of it for further calculation or whatever.
i.e
Private Sub sub1()
//You use the Sub2's value in here like
msgbox(sub2)
End Sub
Private Function sub2() As String //You can specifiy any data type you are returning, I specified String just for demnostrate
//Set the value of Sub2 in here
sub2 = "Yourvalue";
End Function

How to call a function with function name reference to a string value in VB

I have this algorithm that I want to implement on VB6.
Sub Main()
dim stringVal1 as string, stringVal2 as string
dim getOne as boolean
stringVal1 = "FunctOne"
stringVal2 = "FunctTwo"
if getOne then
'Call Function with function name assigned to stringVal1 ... how to call the function here?**
else
'Call Function with function name assigned to stringVal1 ... how to call the function here?**
end if
End Sub
Function FunctOne()
Msgbox "I'm function one"
End Function
Function FunctTwo()
Msgbox "I'm function two"
End Function
Can this be done in VB6?
Generally, such code patterns point to errors in your software design.
In the rare cases where this is really needed, CallByName accomplishes this.
Example:
Call CallByName(Me, "NameOfFunction", vbMethod, arguments)
It would help if you give more information about why you're needing to call functions by the string representation of their name. Could you not simply re-write your code like this:
If getOne Then
Call FuncOne()
Else
Call FuncTwo()
End If
Generally, you don't want to actually dispatch based on Strings. This is error-prone (as you have to be really sure at run time that your String has the function name you want), and really unnecessary. The way to do this sort of thing where you want to be able to "pass in" the method that you want to use would be to use the polymorphism that VB has, and use a separate Class for each implementation.
In the Class Module that you use for your interface, say named MyInterface:
Public Sub DoStuff()
End Sub
Then, create two Class Modules, one for each possible implementation of the Interface:
In MyClassOne:
Implements MyInterface
Public Sub MyInterface_DoStuff()
Msgbox "I'm function one"
End Sub
Then, in MyClassTwo, the same thing but with your other implementation:
Implements MyInterface
Public Sub MyInterface_DoStuff()
Msgbox "I'm function two"
End Sub
To use it, you just need to pass in which implementation you want to use:
Private Sub UseInterface(WhichThingToUse as MyInterface)
WhichThingToUse.DoStuff
End Sub
Rather than using a String variable to store which method to use, you need to instead store a New MyClassOne or a New MyClassTwo. You don't show how your getOne variable gets set, but whatever the logic is for it, you just need to change to store an instance of MyInterface instead, and just use that directly.
For further reading in the MSDN library:
How Visual Basic Provides Polymorphism
Creating and Implementing an Interface
Creating Interfaces for Use With the Implements Statement
Implements Statement reference

Resources