2

A simple question with a if inline: dim mydate as datetime?


'Version 1 (WORKS !)

If dtReader.IsDBNull(dtReader.GetOrdinal("mydate")) Then
    mydate = Nothing
Else
    mydate = dtReader.GetDateTime(dtReader.GetOrdinal("mydate"))
End If

Value = nothing


'Version 2 (DOENSN'T WORK !)

mydate = If(dtReader.IsDBNull(dtReader.GetOrdinal("mydate")), Nothing, dtReader.GetDateTime(dtReader.GetOrdinal("mydate")))

Value = #12:00:00#


Can someone explain me why the version 2 get this value ?

varocarbas
  • 12,354
  • 4
  • 26
  • 37
Testman
  • 57
  • 7
  • 1
    It doesn't make any sense. Check your input information (e.g., if you are writing one code after the other one, they would be analysing different rows). Inline or multiline conditions do certainly have the same behaviour; just use them properly. – varocarbas Dec 09 '15 at 10:00
  • Not really clear, you right, edited, thx – Testman Dec 09 '15 at 10:00
  • They both look exactly the same, except one uses the ternary if. Please can you provide enough code to reproduce the problem? You could always look at the IL to see the difference. – Sam Makin Dec 09 '15 at 10:06
  • 1
    So can you tell me why i got the same shit with this : If(True, Nothing, Now) – Testman Dec 09 '15 at 10:09
  • @SamMakin's answer proves that there is a really weird difference between both types of conditions for nullable dates!! Really strange. In any case, the proposed conditions are too unclear (might easily be referring to different inputs). – varocarbas Dec 09 '15 at 11:02
  • After some tests and the discussion in the damien_the_unbeliever answer, I understood the problem: when assigning a DateTime type in the second part of the ternary operator, you are forcing VB.NET to think that it is DateTime, rather than DateTime?. If you cast to DateTime? you would avoid this problem (what is exactly what you should be doing, as far as assigning to a different type is not right). This whole situation became a bit confusing because VB.NET (Option Strict On/Off) doesn't recognise this issue. On the other hand, you should always cast/convert to the right type, so... – varocarbas Dec 09 '15 at 12:00

2 Answers2

6

This comes down to the compiler having to do type analysis on the If. Bear in mind that Nothing is not the same as C#'s null, it's closer to default(T):

If a variable is of a value type that is not nullable, assigning Nothing to it sets it to the default value for its declared type

Now, when the compiler is analysing If, it's got to decide on the type of the whole expression. Here's what it's looking at:

If(Boolean,<AnyType>,DateTime)

Now, it has to decide what the type of the expression is, based on the types of the second and third parameters, and it has to pick one of the types so present. So, quite naturally, it picks DateTime. And a Nothing converted to a DateTime is the same as the min value.

To change this, give it the choice to deduce the type as DateTime? instead:

mydate = If(dtReader.IsDBNull(dtReader.GetOrdinal("mydate")), _
          CType(Nothing,DateTime?), _
          dtReader.GetDateTime(dtReader.GetOrdinal("mydate")))

Per the Visual Basic Language Specification, section 11.22 (Conditional Expressions):

If three operands are provided, all three expressions must be classified as values, and the first operand must be a Boolean expression. If the result is of the expression is true, then the second expression will be the result of the operator, otherwise the third expression will be the result of the operator. The result type of the expression is the dominant type between the types of the second and third expression. If there is no dominant type, then a compile-time error occurs.

(My emphasis).

And note that there's no conditional text about "if this is being used in an assignment statement, you may also take into account the declared type of the variable being assigned".

Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448
  • But the question is: why when you do `mydate = Nothing`, it gets `Nothing` rather than the logical minimum date value? Why the behaviour of the ternary operator is different? In my opinion, this is a bug (although a not too relevant one): the ternary operator should always deliver the same result than a condition (a normal assignation). – varocarbas Dec 09 '15 at 11:20
  • @varocarbas - It's not a bug. First you have to decide on the type of the `If` expression, and that is solely determined by the types of the second and third parameters. It doesn't (and cannot, generally) also take into account the fact that its result is going to later be assigned to a specific variable. – Damien_The_Unbeliever Dec 09 '15 at 11:26
  • You are trying to justify the behaviour by bringing into account your knowledge about the specific implementation, but in my opinion this is irrelevant here. As a programmer, I expect ternary operators to deliver exactly the same results than other conditions. When I convert any set of if... else into a ternary operator, I want to get exactly the same result; any other behaviour would be a bug to me (= against my reasonable expectations). The fact that the bug is unintentional (i.e., error while building the compiler) or intentional (i.e., flaw in the logic) is in my opinion irrelevant. – varocarbas Dec 09 '15 at 11:34
  • @varocarbas - no, consider this statement - `Dim abc = If(a=b,c,d)`. The compiler *has* to be able to analyse the `If` operator and determine the type of it result before it can even know what the type of `abc` is. The `If` operator can be used in *any* position where an expression is valid and has to be capable of being analysed independently. It is not strictly scoped to only be used on the right hand side of an assignment. – Damien_The_Unbeliever Dec 09 '15 at 11:36
  • Test this code: `Dim d As DateTime? = New DateTime? d = If(True, Nothing, Now) Dim d2 As DateTime? = New DateTime? If True Then d2 = Nothing Else d2 = Now End If If d = d2 Then 'Never reached End If` it delivers: d = minimum date and d2 = nothing and d different than d2. To me this is a bug. I want both conditions to deliver exactly the same or, in the worst scenario, to deliver results which might be considered equal. This is my understanding of how ternary operators should work (and how I want them to work). – varocarbas Dec 09 '15 at 11:40
  • 1
    @varocarbas - the language is working as it is specified to. That it doesn't match your expectations is neither here nor there. I've tried to explain *why* the language is designed this way, and why, therefore, it doesn't match your expectations. I don't know how else to say that the evaluation of the types in the `If` operator and *then* the analysis of the assignment are, necessarily, decoupled. – Damien_The_Unbeliever Dec 09 '15 at 11:47
  • This is equivalent - If(True, DirectCast(Nothing, Date?), Now) – Sam Makin Dec 09 '15 at 11:48
  • 1
    @SamMakin - yes, `DirectCast` can also be used. You can even cast the `Now` rather than the `Nothing`. The key part is to give the compiler the choice of `DateTime?`. – Damien_The_Unbeliever Dec 09 '15 at 11:49
  • 1
    In fact, I have been doing some tests with C# and I understood the problem. If you do: `d = If(True, Nothing, CType(Now, DateTime?))` it behaves exactly as expected. When you assign `Now` directly, you are forcing the ternary operator to think that it is dealing with a `DateTime` rather than with a `DateTime?`. – varocarbas Dec 09 '15 at 11:51
  • Perhaps not exactly a bug, but two negative points for VB.NET here (some times it wins, some times it loses): firstly not properly recognising this situation (`Option Strict On/Off` doesn't matter here); secondly, forcing me to use `CType` (`DirectCast` doesn't work in this specific case? Why?). – varocarbas Dec 09 '15 at 11:53
  • @varocarbas - it's very difficult to write a C# equivalent with making the types explicitly obvious, because as I say, `Nothing` is akin to C#'s `default(T)`, and you're having to name the type there. – Damien_The_Unbeliever Dec 09 '15 at 12:00
  • 1
    My point was that C# didn't allow me to do such a thing (complained about `Now` not being `DateTime?`) and that's why I realised about the problem. As said, the behaviour is fine (i.e., the weirdness happens because there is no conversion/casting to the proper type), but VB.NET should warn about this issue (i.e., not allowing this behaviour with `Option Strict Off`, what C# is rightly doing). – varocarbas Dec 09 '15 at 12:02
2

Switch Option Strict ON! Implicit conversions are going on.

See this answer for an example.

Update: If the type you are setting isnt nullable these two if statements are exactly the same. If they ARE nullable (DateTime is not by default) then the two if statements produce different results. Example:

Test 1:

Code:

Dim d As DateTime?

d = If(True, Nothing, Now)

Result:

DateTime? dateTime = new DateTime?(DateTime.MinValue);

Test 2:

Code:

Dim d As DateTime?
If True Then
    d = Nothing
Else
    d = Now
End If

Result:

DateTime? dateTime = null;
Community
  • 1
  • 1
Sam Makin
  • 1,526
  • 8
  • 23
  • You are right - it doent warn with Option Strict On. The cause is still the conversion though. It is returning a new Datetime instead of Nothing – Sam Makin Dec 09 '15 at 10:18
  • 2
    This is certainly weird and your answer worthy (also the question). In any case, the Strict On/Off issue doesn't have any effect here. – varocarbas Dec 09 '15 at 11:03