I am debugging a project right now and it has a function with the following signature:
Public Function changeRemoteDirectory(ByVal newDirectory As String, Optional ByVal Direction As Boolean = True) As Boolean
MsgBox(direction)
'rest of code
End Function
I was trying to figure out what was causing this function to return a value of False when I knew that it should return True given the input that I was supplying, so I put MsgBox(direction) into the Function to see what the value of direction was when I called the Function. I called the function like this, yet I received a MsgBox that showed the value of direction to be False:
changeRemoteDirectory("/internal")
The first parameter works just fine and the code that requires it to execute works correctly, but I cannot figure out why Direction has a value of False in a situation where, I believe, it should have its default value of True. I'm not totally opposed to rewriting the Function if need be, but can anybody here discern why Direction does not have a value of True when the function changeRemoteDirectory() is called without the second parameter supplied?
It sounds like you're experiencing one of many pains that optional parameters cause.
When the code that calls changeRemoteDirectory is compiled, the optional parameter is injected into the calls just like a const would be replaced at compile time. Changes to the value of an optional parameter will not take effect without recompiling the caller.
See this article for more info.
In general, you should use method overloads instead of optional parameters. You get all of the same functionality without the pain and performance drawbacks.
Related
Is there a chance that existing code in a large project might bomb if I add a new optional parameter to a function that's used everywhere? I know I can overload the function instead and minimize the risk, but really.. what's the risk if I insist on going with an optional parameter?
Here's an example:
Public Function GetContent(ByVal URL As String, ByVal ID As String, Optional ByRef PageTitle As String = "") As String
Try
Dim web As New HtmlWeb()
Dim doc As HtmlDocument = web.Load(URL)
ID = "//div[#id='" & ID & "']"
Dim ContentNode As HtmlNode = doc.DocumentNode.SelectSingleNode(ID)
' The two lines below are the mere extent of what's new inside this function, besides the new Optional ByRef parameter in its signature
Dim PageTitleNode As HtmlNode = doc.DocumentNode.SelectSingleNode("//title")
If Not PageTitleNode Is Nothing Then PageTitle = PageTitleNode.InnerHtml
Return ContentNode.InnerHtml
Catch ex As Exception
Return "<h4> Bad. Very bad. </h4>"
End Try
End Function
PS: I'd like to comment on my question after the fact, having read others' responses below and having done some additional research myself. Originally, I didn't want to question the validity of the approach of using an optional parameter. That was something VB.NET was allowing me to do and I felt I had every right to use--besides that it was simply very convenient! But my original question had more to do with whether there may be gaps in how optional parameters are implemented, from compilation down to execution--gaps that I should consider as I design my code. I was unaware of the historical significance of the optional parameter approach in relation to the overload approach. I've learned now that it's not that there are gaps or flaws in the optional parameter approach; rather, it was a solution designed for a different and older set of concerns that was simply overridden with the advent of the Common Language Runtime. I'm using VS2013. Sure, everything compiled fine with the optional parameter approach and seemed to run fine but I wanted to confirm I wasn't potentially breaking something else by adding an optional parameter--especially since someone looked at my code and suggested I should overload the function instead. I wanted to prove why I shouldn't keep my optional parameter method. James Thorpe answered that question for me now, I think. But as Tim Schmelter asked, is there a benefit for doing it this way (optional parameters) as opposed to the overload approach? To me now the overload approach seems the best and only way, and that is because I'm using a newer set of technologies that the optional parameter approach--which was implemented for Microsoft's older Component Object Model, or COM--simply wasn't designed to address (see page 83 of the book, "Microsoft Visual C# 2013 Step By Step" by John Sharp). Particularly now, if there are external modules expecting to find the old function signature (i.e., the function parameter layout that existed before I added the new optional parameter), they'll break unless I recompile them too! That's a hindrance for me. But overloading handles this software development problem much better without need for recompilation, something only now supported by the newer Common Languange Runtime, or CLR. I suppose the optional parameter support in VB.NET is more of a historical holdover now from the old COM days--and not the very best solution for my specific requirements. I've also just learned that, "The Common Language Specification, which defines the subset of the CLR that all languages should support, explicitly disallows a reliance on optional parameters. This means they are not a candidate for use in the Base Class Library and will probably never been seen in any of the other libraries shipped as part of the .NET Framework." (from the online article, "Optional Parameters Are Gaining Ground in .NET", by Jonathan Allen). Although the rules are laxer for us regular developers that consume Microsoft technologies, I think there's something to be said for their internal decision not to rely on optional parameters. I just wanted to post and share that with you in case like me you've also come here wondering!
Within a single project? No, it should be fine. However, in the comments you said:
Let's say there were other projects calling it (there is a possibility). Would it break those if I didn't rebuild them?
Optional parameters are actually baked in at compile time, so if you have your original method signature:
Public Function GetContent(ByVal URL As String, ByVal ID As String)
And someone is calling it thusly:
GetContent(someUrl, someId)
It will be compiled into their assembly as-is. With your new optional parameter, anything calling it as above without passing in the parameter would actually get compiled as:
GetContent(someUrl, someId, "")
Note how the default value of the optional parameter has automatically been brought in. If you're rebuilding everything, it's all good. However, in those projects that are referencing this one that aren't rebuilt, they will have the original two-parameter call. Your GetContent method now requires 3 parameters at runtime - you'll get a runtime error as it can't find an overload of the function that still takes 2 parameters.
Here's how you can do it without breaking code...
Public Function GetContent(ByVal URL As String, ByVal ID As String, ByRef PageTitle As String = "") As String
' the rest of your function here
End Function
Public Function GetContent(ByVal URL As String, ByVal ID As String) As String
Return GetContent(URL, ID, "")
End Function
That way you have a version of the function with 2 parameters for the existing code and one with 3.
If you want to encourage programmers to switch to the 3 parameter version, then you can mark the 2 parameter version like this:
<Obsolete("Use the version that takes pageTitle as a 3rd parameter">
Public Function GetContent(ByVal URL As String, ByVal ID As String) As String
Return GetContent(URL, ID, "")
End Function
That will give you a compiler warning if you try to call the 2-parameter version.
i have a Function with1 Parameter and 10 optional Parameters.
The optional Parameters are all nothing.
Now i want to check if a optional Parameter had changed his Value or the Function was started with an optional Parameter.
Think of it that the Parameters can have the Value Nothing then the Function returns too.
ty for your help. :D
10 parameters.. that's a bit excessive... and hard to manage.
You would be better passing a class or structure.
Checking the optional parameter is the default value is the usual method for this for determining if it is pre-set or not as in the previous answer.
You would need to pass an object by reference if you need to test if it changed while the routine was running, but if it was originally passed as "Nothing" that will not work.
Without more information on what your usage intent is, it is a little hard to answer this conclusively.
If Parameter1 Is Nothing then
'Parameter1 changed
EndIf
If your other optional parameters is nothing I trust that their reference type is a string.
The following from MSDN to check if an optional argument is present -
A procedure cannot detect at run time whether a given argument has been omitted or the calling code has explicitly supplied the default value. If you need to make this distinction, you can set an unlikely value as the default. The following procedure defines the optional parameter office, and tests for its default value, QJZ, to see if it has been omitted in the call:
Sub notify(ByVal company As String, Optional ByVal office As String = "QJZ")
If office = "QJZ" Then
Debug.WriteLine("office not supplied -- using Headquarters")
office = "Headquarters"
End If
' Insert code to notify headquarters or specified office.
End Sub
Link can be found HERE
I've been using Visual Studio and I work with VB. Now I've noticed something called text:= in the IntelliSense suggestion list it gives me when coding. I'm not sure what it is. Could anyone explain it to me?
It allows you to specify the value of a particular parameter when passing arguments to a method. Normally, the parameters are determined by the order of the arguments. For instance, if you had a method like this:
Public Sub WriteStrings(s1 As String, s2 As String)
Console.AppendLine(s1 & s2)
End Sub
You would normally just call it like this:
WriteStrings("A", "B") ' Outputs "AB"
However, you could call it with named parameters like this:
WriteStrings(s1:="A", s2:="B") ' Outputs "AB"
In that example, the only real advantage is that it is obvious, when looking at the code, which argument is being passed for each parameter. However, it also allows the interesting possibility of passing the arguments in a different order. For instance:
WriteStrings(s2:="A", s1:="B") ' Outputs "BA"
However, the most common place you'll see it is when the parameters are optional. For instance, when calling this method:
Public Sub DisplayPerson(Optional includeName As Boolean = True,
Optional includeAge As Boolean = False,
Optional includeAddress As Boolean = True)
And you want to leave the default settings for the first two parameters and just want to force includeAddress to be False, you could just call it like this:
DisplayPerson(, , False)
But that's a little confusing. Many people find it less confusing to specify the name of the parameter to make the code easier to read:
DisplayPerson(includeAddress:=False)
Since arguments to set the properties of an attribute have no particular order, it's very common to see named arguments used there as well:
<DataContract(Name:="Test")>
I have a VBA function called getYear that is initialized as follows :
Public Function getYear(ByVal year As Date, Optional ByVal month As Date) As Long
Then, inside the function I have :
msg = MsgBox(IsEmpty(month), vbOKOnly) So I know whether or not the month has been input in the function.
The only thing is whether I have a function call such as : getYear(currentYear) or getYear(currentYear, currentMonth) isEmpty is always returning False
If I am understanding this correctly when I call getYear(currentYear) isEmpty() should return True, but it is not...
I gather that is from my function initialization, but I don't know what else to do.
Any help is appreciated!
From MSDN:
IsEmpty only returns meaningful information for variants
You can also use IsMissing() to check if an optional argument was passed to the function, but this works only on Variants as well. You might want to check if the optional argument has a value equal to his default value, which is zero for dates.
You can create a default value for "month" that is zero or negative, or out of the range of possible entries. Then instead of using IsMissing or IsEmpty, you can just test for that default value. If month = the default, you know that parameter was not passed to the function.
When in C# we have the out and ref parameter options, in VB there is a only one: ByRef.
Now, little 'problem' when trying to 'eliminate' the compiler warning saying that test was not initialized before passing as argument:
Dim test As MyParsableClass ' = Nothing need imperatively?? '
' some code ... '
MyParsableClass.TryParse("value", test) ' warning on "test" here
the class brief declaration:
Class MyParsableClass
Public Shared Function TryParse(ByVal value As String, _
ByRef myParsableClass As MyParsableClass) As Boolean
myParsableClass = Nothing
If True Then
' parse code OK'
myParsableClass = New MyParsableClass()
Return True
Else
' parse code NOK '
' myParsableClass remains Nothing '
Return False
End If
End Function
End Class
maybe a solution was to declare
...Optional ByRef myParsableClass As MyParsableClass = Nothing)
but I can't set this parameter as optional one. What will happen if I'll miss it?
PS. (edit)
In the real project, my "parsable" class is MyHour with Hour and Minute properties. I wrote already the Parse(value as String) with a FormatException, but I think the code could be more clear, compact and quick when I will not use try catch blocks...
I do not believe it's possible to prevent this warning, without an explicit assignment.
Different languages have different features/facilities - if they didn't, there'd only be one programming language :-) In this case, yes, VB doesn't pretend that there are two types of ref parameters, as C# does - so far as the CLR is concerned, "out" doesn't exist.
And I'm not sure what peSHIr is talking about - TryParse was added to later releases of the BCL for precisely the situation where a parse is as likely to fail as to succeed - so you can take a faulting path without requiring an exception to be thrown.
Edit
To add - the reason you don't get a warning for many of the built in types for which a TryParse exists (e.g. Int32) is because they're Structs/Value types, and hence always have a value. If your class is simple enough, would it be logical for it to be a Structure instead?
Not exactly an answer to your question, but out and ref/ByRef are bad, so why use them in the first place? Many developers think that the TryParse paradigm in the .NET Framework 1.0 was a bad way to go.
Why not go for a MyParsableClass that has a Public Shared Function Parse(ByVal value As String) As MyParsableClass method that raises an appropriate exception when needed?
Or even a Public Shared Function Parse(ByVal value As String) As MyParsableClassParsed where, MyParsableClassParsed is a helper inner class that contains two readonly properties: Success As Boolean and Result As MyParsableClass? You could then always get a result from calling Parse, but you'd get Success==True and Result==[whatever], or simply Success==False and Result==Nothing.
Also, your MyParsableClassParsed helper class could also use an enumerator instead of a boolean and/or a list of error messages to tell the caller how/why the parse operation failed. Or the throw exception might have such an enumerated value and/or error message(s).
Much easier to use and more flexible. And all without ByRef to give you headaches/warnings.