Chance of breaking existing code by adding optional parameter to VB.NET function? - com

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.

Related

No binary compatibility although a declaration is kept identical

I'm stuck on trying generating a new version of a COM DLL with binary compatibility. I don't understand why I get this message :
'init' in the 'Logger' class module has arguments and/or a return type that is incompatible with a similar declaration in the version-compatible component.
Original definition:
Function init(aLOGDIR As String, Optional aListBox As Object, Optional aMAXLISTBOXLINES As Integer) As Boolean
Current definition:
Function init(aLOGDIR As String, Optional aListBox As Object, Optional aMAXLISTBOXLINES As Integer) As Boolean
I haven't change init as you can see...
Here's my steps :
First generation without compatibility
Set up a binary compatibility in Project properties (referencing the previous generated dll with or without renaming it)
Second generation
Warning occurs.
Is this because a parameter is an Object ? Thanks for your help.
In my experience, when trying to get VB6 working with COM or C++, one must pay careful attention to the differences in data-types. I'm guessing this might be your issue. I apologize if you are already familiar with this:
aLOGDIR As String implies a char**; consider changing to ByVal aLOGDIR As String, though I doubt this is relevant to your issue.
Optional aMAXLISTBOXLINES As Integer implies a short and not an int. Certain padding issues could arise, but simply changing it to As Long might be sufficient and fix the issue.
As Boolean implies a short and not a bool on systems. It might be safer to just use As Long.

Invoke anonymous methods

Is there any difference under the hood between line 4 and line 5?
Why can't VB.net handle Line 3?
What is the proper way to call the function?
Dim aFunc As New Tuple(Of Func(Of String))(Function() "Hello World")
Dim s As String
s = aFunc.Item1() 'does not compile
s = (aFunc.Item1)()
s = aFunc.Item1.Invoke()
This looks like a compiler bug to me, the parentheses should make it unambiguously a method call. Hard to state this for a fact however, parens are heavily overloaded in vb.net to mean many things. Clearly it is the tuple that makes the compiler fumble, it works fine without it. This came up in this week's StackExchange podcast with Eric Lippert btw, you might want to listen to it to get the laundry list of things it can mean.
You could post this to connect.microsoft.com to get the opinion of the language designers. The behavior is certainly unintuitive enough to call it a bug. The workarounds you found are good. Both generate the exact same code and add no overhead, something you can see by running ildasm.exe on your assembly.
aFunc.Item1 is a Function, so you can't assign it to a String. You appear to want:
Dim aFunc As New Tuple(Of Func(Of String))(Function() "Hello World")
Dim s As String
Dim f As Func(Of String) = aFunc.Item1
s = f.Invoke()
EDIT:
s = aFunc.Item1() accesses the property Item1. To invoke the function which that property refers to, you can use s = aFunc.Item1()(), which is equivalent to your line 4. At a guess, property access is stronger than function invocation (if those are the correct terms).

'out' issue in VB.NET

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.

How to pass a generic type not having a Interface to a Of T function

I have a following code which works fine
MsgBox(AddSomething(Of String)("Hello", "World"))
Public Function AddSomething(Of T)(ByVal FirstValue As T, ByVal SecondValue As T) As String
Return FirstValue.ToString + SecondValue.ToString
End Function
Now we are redesigning the application to work with parameters of different types which will be provided through XML
<SomeValues>
<Add Param1="Somedata" Param2="SomeData" MyType="String"/>
<Add Param1="Somedata" Param2="SomeData" MyType="MyBusinessObject"/>
</SomeValues>
If I try to provide the following it gives error as Of accepts only type
''''Get DetailsFromXml --- MyType,Param1,Param2
MsgBox(AddSomething(Of Type.GetType(MyType))(Param1,Param2))
How to solve this issue.
Edit
The above example is given to make the question simple. Actual issue is as follows
I am using SCSF of P&P.
Following is per view code which has to be written for each view
Private Sub tsStudentTableMenuClick()
Dim _StudentTableListView As StudentListView
_StudentTableListView = ShowViewInWorkspace(Of StudentListView)("StudentTable List", WorkspaceNames.RightWorkspace)
_StudentTableListView.Show()
End Sub
Now I want to show the views dynamically.
Public Sub ShowModalView(ByVal ViewName As String)
Dim _MasterListView As >>>EmployeeListView<<<<
_MasterListView = ShowViewInWorkspace(Of >>>EmployeeListView<<<)("Employee List", WorkspaceNames.RightWorkspace)
_MasterListView.Show()
End Sub
So the part shown using the arrows above has to be somehow dynamically provided.
The point of generics is to provide extra information at compile-time. You've only got that information at execution-time.
As you're using VB, you may be able to get away with turning Option Strict off to achieve late binding. I don't know whether you can turn it off for just a small piece of code - that would be the ideal, really.
Otherwise, and if you really can't get the information at compile-time, you'll need to call it with reflection - fetch the generic "blueprint" of the method, call MethodInfo.MakeGenericMethod and then invoke it.
I assume that the real method is somewhat more complicated? After all, you can call ToString() on anything...
(It's possible that with .NET 4.0 you'll have more options. You could certainly use dynamic in C# 4.0, and I believe that VB10 will provide the same sort of functionality.)
In .Net generics, you must be able to resolve to a specific type at compile time, so that it can generate appropriate code. Any time you're using reflection, you're resolving the type at run time.
In this case, you're always just calling the .ToString() method. If that's really all your code does, you could just change the parameter type to Object rather than use a generic method. If it's a little more complicated, you could also try requiring your parameters to implement some common interface that you will define.
If all you are doing is ToString, then making the parameters object instead would solve the problem in the simplest way. Otherwise you are going to have to bind the type at run-time, which in C# looks like:
System.Reflection.MethodInfo mi = GetType().GetMethod("AddSomething");
mi = mi.MakeGenericMethod(Type.GetType(MyType));
object result = mi.Invoke(this, new object[] { Param1, Param2 });
Because it involves reflection it won't be fast though... but I assume that's not a problem in this context.

What is the use of the := syntax?

I'm a C# developer working on a VB.NET project, and VS keeps trying to get me to use the := thingie when I call a function with a ByRef parameter like so:
While reader.Read()
HydrateBookFromReader(reader:=???)
the HydrateBookFromReader function has the following signature:
Public Function HydrateBookFromReader(ByRef reader As SqlDataReader) As Book
Why does intellisense keep insisting that I use that := construction, and what is it for?
In VB, the := is used in specifying named parameters.
Contact(Address:="2020 Palm Ave", Name:="Peter Evans")
This is especially useful for specifying optional parameters.
Why does intellisense keep insisting that I use that := construction, and what is it for?
It's important to note that IntelliSense doesn't insist, it proposes. Using it in your case wouldn't make sense … this feature is primarily used for very long parameter lists with many optional parameters, of which you only want to pass, say, the last one. It's useful when working with Microsoft Office Interop.
Also (since you mention it in your tags): this has got nothing to do with ByRef. ByRef is equivalent to ref and out in C#, i.e. it allows the method to manipulate the parameter itself.
Intellisense may be suggesting the := syntax, but I suspect that it will compile without it.
HydrateBookFromReader(myReader);
In future versions of C# where optional parameters are allowed, named parameters will allow you to specify some parameters but not others, and to specify parameters in a different order than they were declared. Named parameters will also allow you to optionally clarify the purpose of the parameter being passed in, making the code more readable in some cases.
Named parameters will be especially important in c# 4.0 for COM Interop, where many superfluous parameters can be eliminated.
Anders Hejlsberg has an excellent discussion about the future of C# on Channel 9 at http://channel9.msdn.com/pdc2008/TL16/. His discussion about named parameters is at 40 minutes and 45 seconds into the talk.