'out' issue in VB.NET - 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.

Related

What is Office.Tools.Ribbon.RibbonControlImpl?

I am building a small helper to fetch and populate all the controls of a ribbon group inside a collection.
Option Strict On
Imports Microsoft.Office.Tools.Ribbon
Private Function GetChildControls(Group As RibbonGroup) As IEnumerable(Of RibbonControl)
Dim ChildControls As New List(Of RibbonControl)
Dim SubControls As IEnumerable(Of RibbonControl) = Group.Items
ChildControls.AddRange(SubControls)
' ...
' Some recursive call over SubControls to get the children of each child (not relevant here)
' ...
Return ChildControls
End Function
Code breaks at ChildControls.AddRange(SubControls), with the following exception:
System.InvalidCastException: 'Unable to cast object of type Microsoft.Office.Tools.Ribbon.RibbonControl[]' to type Microsoft.Office.Tools.Ribbon.RibbonControlImpl[]'.'
I cannot find any reference at all on RibbonControlImpl. The Microsoft documentation is silent, and - surprisingly - so are Google or StackOverflow.
Changing ChildControls.AddRange(SubControls) into SubControls.ToList.ForEach(Sub(p) ChildControls.Add(p)) does not work either, but a classic For Each does the trick:
For Each MySubControl As RibbonControl In SubControls
ChildControls.Add(MySubControl)
Next
I would like to understand what is happening here? Is Impl a suffix to interface types to signify something? Maybe 'Impl' stands for 'Implementation'? I could not find any information on this either.
Just some thoughts, no idea if I am right; what do you think?
Office.Ribbon.RibbonControl is an interop interface, hence cannot be used as a generic type across assemblies. A guess would be that Microsoft implemented a non-interop RibbonControlImp wrapper to which RibbonControl is implicitly cast every time it is referenced as a generic type.
No idea how this would be achieved, but that would explain why For Each ... Next does not throw the error, whilst relying on generic collections does.

Datarow's items not being treated as Integer in Extension Method, but exception implies it is being treated as an Integer

I implemented an extension method on Integer (this is a simplified example that also shows the error)
<Extension()>
Public Function IsPositive(ByVal item As Integer) As Boolean
Return item > 0
End Function
I then try to call the extension method on a datarow's item:
Dim dtMyTable As DataTable
dtMyTable = GetInfoFromDatabase()
If dtMyTable.Rows(0).Item("nCount").IsPositive() Then
This gives me the exception:
Public member 'IsPostive' on type 'Integer' not found.
I assume this is because dtMyTable.Rows(0).Item("nCount") is actually an object, not an integer. The exception seems to understand that that isn't the case, so I'm not sure why that's different, but it is.
However, if I try to call the same method as if it's just a regular method, it works without complaint
If IsPositive(dtMyTable.Rows(0).Item("nCount")) Then
I would rather call it the former way. I know it's possible to just save the value to a variable and then call the extension on that variable, but that seems like a needless extra step.
Is there any way to get the former method to work without adding an extra variable assignment every time I need to call it, or changing the extension method to work on Objects?

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

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.

Enforce Type Alias in VB.NET

How might one go about aliasing a type in VB.NET or C# such that when the alias is used as an argument type in a function, accidentally using the not aliased type is an error?
i.e.
Imports AccessSpecifier = System.String
Module Accessors
Delegate Function IoOper(ByRef strm As System.IO.Stream) As Action
Public Function accessMethod(ByRef spec As AccessSpecifier) As IoOper
' implementation
' ...
End Function
End Module
Module Main
Public Sub Main()
Dim spec As AccessSpecifier = New AccessSpecifier(CType("READ_WRITE", Char()))
Dim val = Accessors.accessMethod(spec)
System.Console.WriteLine(val.GetType())
Dim shouldFail = Accessors.accessMethod("FAIL_ME")
System.Console.WriteLine(shouldFail.GetType())
End Sub
End Module
Or perhaps is there a better way to go about this?
Overall, I'm wanting the IDE to force me to know what I'm doing if I'm throwing Ints around to mean Flags, or States and Strings around to mean Names, Propertys and Records.
Help?
I've never liked Type aliasing in .NET. It makes for imprecise code and it is not immediately clear what is happening. As in your example, when an individual went looking for the AccessSpecifier.vb file (or class) they would not find it.
Also, Aliasing only works within YOUR project and only within a single code file. So you would have to define that alias in all the various code files where it was to be used.
A better (as in easier to read, easier to maintain, and more clear of intent) option is to create a class and overload the type conversion operators for automatic conversion to/from String. In this manner you retain your ability to use your new class as if it were a String, but you gain your strict type checking.

Weird equality issue with Generics and Enums?

I'm kind of going nuts here. I have a function something like the following. It's failing to return an object. I can pass in a list, I can see in QuickWatch that x.RB = theRb for at least one of the items in the list, yet it doesn't exit the loop (via the Return). The loop continues.
The list I am passing in is a subclass of aXXX.
Property RB on class aXXX is of type RBEnum.
Also, I originally used Linq for this but was getting "no matching items" exceptions.
Private Shared Function GetX(Of T As aXXX)(ByVal a As List(Of T),
ByVal theRb As RBEnum) As T
For Each x As T In a
If (x.RB = theRb) Then Return x
Next
Return Nothing
End Function
Any suggestions or ideas on why this isn't working?
I would recommend trying:
If (x.RB.Equals(theRb)) Then Return x
Can you cast the Enum into an Integer and then compare?
If CInt(x.RB)=CInt(theRb) Then Return x
I'm not sure how your original where statement was written but this should produce the result you're looking for:
Private Shared Function GetX(Of T As aXXX)(ByVal a As List(Of T),
ByVal theRb As RBEnum) As T
Return a.Where(Function(x) x.RB = theRb).FirstOrDefault()
End Function
I have resolution. I can't fully explain it though.
The list of items I'm passing in are a subclass of the class aXXX. The subclass did not properly override the RB property from the base class -- no Overloads / Overrides / Shadows. This kind of gives explanation as to why QuickWatch reports True on the match -- maybe this subclass property was hiding the "real" property value that was in the test?
Anyway, by taking out the property in the subclass all together or adding an Overloads, the For Each behaves as one would expect. I can even go back to the original Linq version I had in the function.
I guess this came down to oversight / sloppy coding on my part. But the issue was masked pretty well by the fact that QuickWatch reported "false positives"!
Thanks to everyone for the suggestions and help.