Restrict parameter string to an array of strings at compile-time - vb.net

I have a method with a parameter as a string data-type. I there a convenient way that I can limit the strings the function will accept for the said parameter?
It is my intention that my Visual Studio code editor will underline the invalid string parameter from the function call (just like it would if you defined an expected object or Boolean parameter as a string).
Public Sub SomeSub(someParameter As String)
' Some action.
End Sub
Public Sub AnotherSub()
' Accept only these strings.
SomeSub("The capital of France is Paris.")
SomeSub("The capital of England is London.")
SomeSub("The capital of Italy is Rome.")
' The following string shouldn't be accepted.
' i.e. String is underlined with an error.
SomeSub("I like turtles.")
End Sub
I've researched arrays but they're generally performed from the point the function is being called or after the parameter has been passed, neither of which will suffice as they only allow me to indicate an incorrect parameter to user which is pointless. Considering I'm trying to inform the coder, I need the coder's error to be underlined as the coder finishes typing it.
I'd like to know if there's a direct and convenient means of achieving this without something as complex as a separate function or an additional dozen lines of code.

I am afraid that there's no way to enforce such behavior with strings at compile-time.

Related

Subroutine will not compile

I'm using Access VBA, and I keep getting
Compile error: Argument not optional
whenever I try to pass a collection into a function. What is going on?
Private Sub btnTest_Click()
Dim GarbageLanguages As New Collection
GarbageLanguages.Add "VBA"
PrintCollectionCount (GarbageLanguages) '<-- error happens here
End Sub
Public Sub PrintCollectionCount(c As Collection)
Debug.Print c.Count
End Sub
Short Answer
Remove the parentheses from the following line:
PrintCollectionCount (GarbageLanguages)
Long Answer
For better or worse (mostly worse), VBA has both functions and subroutines:
Function - expression that must return a value
Subroutine - statement that cannot return a value
Unfortunately, using each of them requires slightly different syntax. Suprisingly, this is not a valid subroutine call:
Subroutine(arguments)
Instead, you need to use one of these two options:
Call Subroutine(arguments)
Subroutine arguments
It's even more unfortunate that when you use the wrong syntax, all you get is extremely cryptic error messages. Finally, it's also hard to get used to not using parenthesis because single arguments that are primitive types instead of objects actually work fine:
Subroutine(SomeString) ' works
Subroutine(SomeInteger) ' works
Subroutine(SomeObject) ' does not work
Subroutine(SomeString, SomeInteger) ' does not work
Aside from memorizing the awful error messages, you can try to train yourself to look out for whenever a space gets automatically inserted after the subroutine's name. This:
Subroutine(argument)
gets changed to this:
Subroutine (argument) '<-- RED FLAG

Error using isNumeric() in VB

I am using the IsNumeric() function in visual basic 2012.
my code is this
Dim input As String = "123"
If isNumeric(input) Then
'number codes
Else
'not a number codes
End If
and i'm getting an error on the isNumeric(input) part
isNumeric is a namespace and cannot be used as an expression
i just want to know what is wrong with this, i cant find any documentation that this function has already changed or something.
It sounds like you've created a name clash. You have presumably named your project 'IsNumeric'. The root namespace for the project is named after the project by default so you now have a root namespace named 'IsNumeric' and that takes precedence over the IsNumeric method.
There are a number of options to fix this. Firstly, you can change the root namespace for the project to something other than 'IsNumeric', which you would do in the project properties. Alternatively, you can qualify the method name with its namespace, its module or both, i.e. use Microsoft.VisualBasic.IsNumeric, Information.IsNumeric or Microsoft.VisualBasic.Information.IsNumeric.
I'd tend to suggest not using IsNumeric anyway. It can't distinguish between types of numbers and provides no access to the actual numeric value. If you need to do any of that sort of thing then call the appropriate TryParse method instead, e.g.
Dim number As Double
If Double.TryParse(someText, number) Then
'The input was a valid Double and the value is in 'number'.
Else
'The input was not a valid Double.
End If
Note that IsNumeric actually calls Double.TryParse internally and is the reason it was created in the first place. That's why calling IsNumeric and then something like CDbl is bad: you're parsing the same text twice in that case.
It's very strange, because IsNumeric is a standard function available in VB.Net. Try to create a new console application:
Sub Main()
Dim str As String = "123"
If (IsNumeric(str)) Then
End If
End Sub
For me it works.

Is it better to declare the DataType of a function?

In VB .NET, What is the difference between declaring the functions's data type and ignoring it, i mean is it declared as an Object like the Variables or like something else? to be clearer which function of these two is better:
Private Function foo(ByVal text As String)
Return text
End Function
Private Function foo2(ByVal text As String) As String
Return text
End Function
Does the first one declared "As Object"? and if so, that means the second one in better, right?
The second is clearly better, the first exists only for backwards compatibility reasons. It is only allowed with Option Strict set to Off which is not recommended anyway.
This is the compiler error you normally get:
Option Strict On requires all Function, Property, and Operator
declarations to have an 'As' clause
The return type is Object for the first.

Excel VBA store functions or subroutines in an array

In C/C++, when I have a bunch of functions (pointers), I can store them in an array or a vector and call some of them together in a certain order. Can something similar be done in VBA?
Thanks!
Yes, but I don't recommend it. VBA isn't really built for it. You've tagged this question with Excel, so I will describe how it is done for that Office Product. The general concept applies to most of the Office Suite, but each different product has a different syntax for the Application.Run method.
First, it's important to understand the two different methods of dynamically calling a procedure (sub/function) and when to use each.
Application.Run
Application.Run will either run a subroutine or call a function that is stored in a standard *.bas module.
The first parameter is the name of the procedure (passed in as a string). After that, you can pass up to 30 arguments. (If your procedure requires more than that, refactor for the love of code.)
There are two other important things to note about Application.Run.
You cannot use named arguments. Args must be passed by position.
Objects passed as arguments are converted to values. This means you could experience unexpected issues if you try to run a procedure that requires objects that have default properties as arguments.
Public Sub Test1()
Application.Run "VBAProject.Module1.SomeFunction"
End Sub
The takeaway:
Use Application.Run when you're working with a standard module.
VBA.Interaction.CallByName
CallByName executes a method of an object, or sets/gets a property of an object.
It takes in the instance of the object you want to call the method on as an argument, as well as the method name (again as a string).
Public Sub Test2()
Dim anObj As SomeObject
Dim result As Boolean
result = CallByName(anObj, "IsValid")
End Sub
The takeaway:
Use CallByName when you want to call a method of a class.
No pointers.
As you can see, neither of these methods use actual pointers (at least not externally). They take in strings that they then use to find the pointer to the procedure that you want to execute. So, you'll need to know the exact name of the procedure you want to execute. You'll also need to know which method you need to use. CallByName having the extra burden of requiring an instance of the object you want to invoke. Either way, you can stores these names as strings inside of an array or collection. (Heck, even a dictionary could make sense.)
So, you can either hard code these as strings, or attempt to extract the appropriate procedure names at runtime. In order to extract the procedure names, you'll need to interface with the VBIDE itself via the Microsoft Visual Basic for Applications Extensibility library. Explaining all of that here would require far too much code and effort, but I can point you to some good resources.
Articles & SE Questions:
Chip Pearson's Programming The VBA Editor
Extending the VBA Extensibility Library
Ugly workaround to get the vbext_ProcKind is breaking encapsulation
Automagic testing framework for VBA
How to get the procedure or function name at runtime
Import Lines of Code
Meta Programming in VBA: The VBIDE and Why Documentation is Important
The code from some of my Qs & As:
vbeCodeModule
vbeProcedure
vbeProcedures
A workaround is to enumerate and use a switch statement. You can store enumerated types (longs) in an array. E.g.:
Enum FType
func1
func2
func3
End Enum
Sub CallEnumFunc(f As FType, arg As String)
Select Case f
Case func1: MyFunction1(arg)
Case func2: MyFunction2(arg)
Case func3: MyFunction3(arg)
End Select
End Sub
Dim fArray(1) As FType
fArray(0) = func1
fArray(1) = func2
CallEnumFunc fArray(1), "blah"

Should I use Call keyword in VB/VBA?

I use the Call keyword when calling subs in VB/VBA. I know it's optional, but is it better to use it or leave it off? I've always thought it was more explicit, but maybe it's just noise.
Also, I read this on another forum: Using the Call keyword is faster because it knows that it is not going to return any values, so it doesn't need to set up any stackspace to make room for the return value.
Ah ha. I have long wondered about this and even reading a two inch thick book on VBA basically says don't use it unless you want to use the Find feature of the VBE to easily find calls in large projects.
But I just found another use.
We know that it's possible to concatenate lines of code with the colon character, for example:
Function Test(mode as Boolean)
if mode = True then x = x + 1 : Exit Sub
y = y - 1
End Sub
But if you do this with procedure calls at the beginning of a line, the VBE assumes that you're referring to a label and removes any indents, aligning the line to the left margin (even though the procedure is called as intended):
Function Test()
Function1 : Function2
End Function
Using the Call statement allows concatenation of procedure calls while maintaining your code indents:
Function Test()
Call Function1 : Call Function2
End Function
If you don't use the Call statement in the above example, the VBE will assume that "Function1" is an label and left align it in the code window, even though it won't cause an error.
For VB6, if there is any chance it will be converted to VB.NET, using Call means the syntax doesn't change. (Parentheses are required in VB.NET for method calls.) (I don't personally think this is worth the bother -- any .NET converter will at least be able to put in parentheses when required. I'm just listing it as a reason.)
Otherwise it is just syntactic sugar.
Note the Call keyword is likely not to be faster when calling some other method/function because a function returns its value anyway, and VB didn't need to create a local variable to receive it, even when Call is not used.
I always use Call in VBA. To me, it just looks cleaner. But, I agree, it's just syntactic sugar, which puts it squarely the realm of personal preference. I've come across probably a dozen full time VBA guys in the past few years, and not one of them used Call. This had the added advantage that I always knew which code was mine. :p
No, it'll just add 7 characters per call with no given benefit.
No one covered this important distinction: in some (common) situations, Call prevents parentheses around function (and sub) arguments from causing the arguments to be strictly interpreted as ByVal.
The big takeaway for you is that if you DO use parentheses around arguments to a routine, perhaps by rote or habit, even though they are not required, then you SHOULD USE Call to ensure that the routine's implicit or explicit ByRef is not disregarded in favor of ByVal; or, instead, you should use an "equal sign" assignment of the return value to prevent the disregard (in which case you would not use Call).
Again, that is to protect you from unfavorably getting ByVal from a routine. Conversely, of course, if you WANT ByVal interpretation regardless of the routine's declaration, then LEAVE OFF the Call (and use parentheses).
Rationale: summarizing "ByRef and ByVal Parameters"
If
1. there is an assignment of a function call retval, e. g.
iSum = myfunc(myArg)
or
2. "Call" is used, e. g.
call myFunc(myArg)
or
call mySub(myArg)
then the parentheses strictly delineate the calling argument list; the routine declaration determines ByVal or ByRef. OTHERWISE the parentheses force ByVal to be used by the routine - even though ByVal was not specified in the routine. Thus,
mySub(myArg) 'uses ByVal regardless of the routine's declaration, whereas
Call mySub(myArg) 'uses ByRef, unless routine declares ByVal
Also note that Call syntactically mandates use of parentheses. You can go
mySub myArg
but you can't go
call mySub myArg
but you CAN go
call mySub(myArg)
(and parentheses are syntactically required for assignment of Function return value)
NOTE however that ByVal on the routine declaration overrides all of this. And FYI, ByRef is always implied in the declaration if you are silent; thus TMK ByRef has no apparent value other than documentary.
Repeating from above: The big takeaway for you is that if you DO use parentheses around arguments to a routine, perhaps by rote or habit, even though they are not required, then you SHOULD USE Call to ensure that the routine's implicit or explicit ByRef is not disregarded in favor of ByVal; or, instead, you should use an "equal sign" assignment of the return value to prevent the disregard (in which case you would not use Call).
Again, that is to protect you from unfavorably getting ByVal from a routine. Conversely, of course, if you WANT ByVal interpretation regardless of the routine's declaration, then LEAVE OFF the Call (and use parentheses).
I use Call for all VBA development of common library functions that I possibly will use in VB.NET. This allows me to move code using copy and paste between all the flavors of VB. I do this to avoid the syntax errors that the code editor creates when it "formats" or "pretty prints" the pasted code. The only edits are usually Set statement inclusion/exclusion.
If you have no plans to move your VB/VBA code to VB.NET, then there is no need to use the Call statement.
The only case I found "call" is useful is quite an accident, about some special operators.
Dim c As IAsyncOperation(Of StartupTask) = StartupTask.GetAsync("Startup")
……
(Await c).Disable()
I got a syntax error for the second line, just like what you'll get with a "New" operator. I really don't want a new variable, which is too inelegant for me. So I tried:
DirectCast(Await c, StartupTask).Disable()
This is syntactically correct. But then the IDE hinted me that the "DirectCast" is unnecessary and gave a simplification. Yes, that is:
Call (Await c).Disable()
That's why I love VS2017 Preview. 😄
If you read the MSDN Support page for the Call Statement, for the specific case o VBA, at least, it does say that Call is optional, but what is very relevant about it and nobody seems to notice is this quoted line:
"If you use either Call syntax to call any intrinsic or user-defined function, the function's return value is discarded."
This is why Call is far from useless. Say you're writing Sub SupportTasks that does a lot of very relevant stuff for you Main Subs (for example, it imports data from a file to be used by different procedures). Now, notice that since SupportTasks is reading external data, there's always a fat chance this data will not come standard and the sub will not be able to fulfill its role. What do you do?
You could, for example, use boolean functions that return False if something goes wrong. Instead of calling a sub, call a function SupportTasks inside and If statement that will exit the Main sub if there's an anomaly:
If Not SupportTasks(SomeArgument) Then
Application.ScreenUpdating = True
Exit Sub
'Else continue the Main sub regularly without writing anything in here
End If
If you're wondering what the heck this has to do with Call, consider the following: in another sub, I call SupportTasks, but I do not need its returned boolean value (for instance, I'm certain an error won't occur). Well, if I don't put it in an If statement or assign the function to a useless variable, VBA will not compile and return me an error (procedure call invalid blah blah blah must assign value to something blah blah blah). That's where Call comes in to save the day!
Call SupportTasks(SomeArgument) '<< "Call Function" call doesn't return an error
If you still think it's useless, think of it as a resource to stay organized. Writing separate procedures for routines shared by many procedures makes your code shorter and more comprehensible, specially when you're writing really large applications. ERPs built out of Excel-Access integrations, for example, can be easier to operate, repair and customize if your IT dept slow to deliver/implement the real system...
To conclude, some internet wisdom:
Always write your code as if the person who will review it is a murderous psychopath who knows where you live.
Amen.
I'm 7 years late to the party, but I just happened to come across the Call keyword a few minutes ago while reading something on MSDN. In particular, it was used to do something I thought was impossible in VB.NET (as opposed to C#) -- which is related to #FCastro's answer.
Class Test
Public Sub DoSomething()
Console.WriteLine("doing something")
End Sub
End Class
Sub Main()
Call (New Test()).DoSomething()
End Sub
In the odd case you don't need the actual object instance but require one of its methods, you can use Call to save a line. Note that this is unnecessary when it's the right-hand side of an operation:
Class Test
Public Function GetSomething() As Integer
Return 0
End Function
End Class
Sub Main()
Dim x As Integer = (New Test()).GetSomething()
End Sub