vblang
vblang copied to clipboard
CDate(CStr(Nothing)) should not cause an exception
Just ran into this today, apparently it's a long standing bug: https://github.com/dotnet/roslyn/issues/2780, although I disagree with the recommended behavior.
IMO the correct fix is for the call to Conversions::ToDate(string) to check to see if the string is null, and return DateTime.MinValue if so.
In my case, Option Strict was Off and the actual assignment that caused the problem was obj.d = if(function(a, b)) where sometimes the return value was nothing and sometimes it was a string that could be interpreted as a date. Obviously the better code would be DateTime.TryParse(function(a,b), obj.d), but it shouldn't break.
The Roslyn team said that the VB compiler of Visual Studio 2013 was doing it wrong in the first place: "This is a bug in the constant optimization phase of the old compiler. It was never the intended behavior of the language." I agree with them. For a start, VBA/VB6 never allowed for it. CDate(vbNullString)
fails with a type mismatch error. CDate(Nothing)
fails with an invalid use of object error. CDate(Null)
fails with an invalid use of null error.
So the VB compiler of Visual Studio 2015 (and later) does it correctly; there is no bug to fix.
If one is stuck with a code-base that has "CDate(CStr(Nothing))" issues, really it should be refactored. It was a poor practice to begin with, and one can't even claim it was ever really okay to do that.
I think https://dotnetfiddle.net/o1Ws5L shows that it’s a bug….only cBool and CDate throw
Public Sub Main()
Dim s as string = nothing
dim bt =CByte(s)
dim c = CChar(s)
dim db = CDbl(s)
dim d = CDec(s)
dim i = CInt(s)
dim l = CLng(s)
dim o = CObj(s)
dim sb = CSByte(s)
dim sh = CShort(s)
dim sn = CSng(s)
dim str = CStr(s)
dim ui = CUInt(s)
dim ul = CULng(s)
dim us = CUShort(s)
Console.WriteLine("no exception")
try
dim b = CBool(s)
catch e as exception
Console.WriteLine(e.ToString())
end try
try
dim dt= CDate(s)
catch e as exception
Console.WriteLine(e.ToString())
end try
End Sub
It may be a more compelling case to say that CBool and CDate are correct, and that the CByte thru CUShort cases (not including CStr and CObj) are wrong.
Let CPdq()
mean to be any one of these CByte thru CUShort cases. Then something like CPdq(CStr(Nothing))
should evaluate to the same result as CPdq("")
- exactly as CBool and CDate do. Why? Because VB.NET idioms should accommodate as many VBA/VB6 idioms as were conceivably possible under the new .NET paradigm. For example, VB.NET has a version of the VBA/VB6 constant vbNullString; hence, CPdq(CStr(Nothing))
means exactly the same as CPdq(vbNullString)
.
Yet in VBA/VB6, things like CInt(vbNullString)
, CDbl(vbNullString)
, and CDec(vbNullString)
, all result in error. So there is no backward-compatibility case (i.e. going from VB6 to VB.NET) to be made for allowing CPdq(vbNullString)
to return a default of "Pdq".
VB.NET, for whatever the reasons, seems to have a special case for CStr(Nothing)
to be treated the same as CObj(Nothing)
for CPdq()
. The Nothing of any other reference type results in error (unless coerced via CObj or however).
It's unclear to me whether this special case for CStr(Nothing)
was a mistake or intentional. The one VBA/VB6 idiom that was not quite brought to VB.NET is that of the COM Variant (https://en.wikipedia.org/wiki/Variant_type_(COM)). Maybe someone thought CStr(Nothing)
could be a stand-in for the Variant Empty
, and make it easier for others to do VB6 to VB.NET translations, especially where a lot of textual processing happens?
Whether intentional or a mistake, I can see why there's a lack of interest and motivation to change CDate and CBool to be more like CPdq.
@rskar-git : I’m not looking for VB6 compatibility, but with VB.Net pre-Roslyn. It changes functional code into non-functional. And VB.NET does extra handling with strings and nothing, this is definitely one of the places I, as a VB.net programmer, would expect it do the useful thing, which is to return the default date, aka date.minvalue. Throwing is not helpful.
Well, to each their own, I guess. I still find code like this to be super-cringy:
Option Strict Off
Dim d As Date
d = vbNullString
even if the above would work at run-time in (pre-Roslyn) VS2013 VB.
(Would it be interesting to point out that the reporter of this issue is happily coding in C# and Rust today?)