= operator applied to nullable types in VB [duplicate] - vb.net

In VB.NET this happens:
Dim x As System.Nullable(Of Decimal) = Nothing
Dim y As System.Nullable(Of Decimal) = Nothing
y = 5
If x <> y Then
Console.WriteLine("true")
Else
Console.WriteLine("false") '' <-- I got this. Why?
End If
But in C# this happens:
decimal? x = default(decimal?);
decimal? y = default(decimal?);
y = 5;
if (x != y)
{
Debug.WriteLine("true"); // <-- I got this -- I'm with you, C# :)
}
else
{
Debug.WriteLine("false");
}
Why is there a difference?

VB.NET and C#.NET are different languages, built by different teams who have made different assumptions about usage; in this case the semantics of a NULL comparison.
My personal preference is for the VB.NET semantics, which in essence gives NULL the semantics "I don't know yet". Then the comparison of 5 to "I don't know yet". is naturally "I don't know yet"; ie NULL. This has the additional advantage of mirroring the behaviour of NULL in (most if not all) SQL databases. This is also a more standard (than C#'s) interpretation of three-valued logic, as explained here.
The C# team made different assumptions about what NULL means, resulting in the behaviour difference you show. Eric Lippert wrote a blog about the meaning of NULL in C#. Per Eric Lippert: "I also wrote about the semantics of nulls in VB / VBScript and JScript here and here".
In any environment in which NULL values are possible, it is imprtant to recognize that the Law of the Excluded Middle (ie that A or ~A is tautologically true) no longer can be relied on.
Update:
A bool (as opposed to a bool?) can only take the values TRUE and FALSE. However a language implementation of NULL must decide on how NULL propagates through expressions. In VB the expressions 5=null and 5<>null BOTH return false. In C#, of the comparable expressions 5==null and 5!=null only the second first [updated 2014-03-02 - PG] returns false. However, in ANY environment that supports null, it is incumbent on the programmer to know the truth tables and null-propagation used by that language.
Update
Eric Lippert's blog articles (mentioned in his comments below) on semantics are now at:
Sep. 30, 2003 - A Whole Lot of Nothing
Oct. 1, 2003 - A Little More on Nothing

Because x <> y returns Nothing instead of true. It is simply not defined since x is not defined. (similar to SQL null).
Note: VB.NET Nothing <> C# null.
You also have to compare the value of a Nullable(Of Decimal) only if it has a value.
So the VB.NET above compares similar to this(which looks less incorrect):
If x.HasValue AndAlso y.HasValue AndAlso x <> y Then
Console.WriteLine("true")
Else
Console.WriteLine("false")
End If
The VB.NET language specification:
7.1.1 Nullable Value Types
... A nullable value type can contain the same values as the non-nullable
version of the type as well as the null value. Thus, for a nullable
value type, assigning Nothing to a variable of the type sets the value
of the variable to the null value, not the zero value of the value
type.
For example:
Dim x As Integer = Nothing
Dim y As Integer? = Nothing
Console.WriteLine(x) ' Prints zero '
Console.WriteLine(y) ' Prints nothing (because the value of y is the null value) '

Look at the generated CIL (I've converted both to C#):
C#:
private static void Main(string[] args)
{
decimal? x = null;
decimal? y = null;
y = 5M;
decimal? CS$0$0000 = x;
decimal? CS$0$0001 = y;
if ((CS$0$0000.GetValueOrDefault() != CS$0$0001.GetValueOrDefault()) ||
(CS$0$0000.HasValue != CS$0$0001.HasValue))
{
Console.WriteLine("true");
}
else
{
Console.WriteLine("false");
}
}
Visual Basic:
[STAThread]
public static void Main()
{
decimal? x = null;
decimal? y = null;
y = 5M;
bool? VB$LW$t_struct$S3 = new bool?(decimal.Compare(x.GetValueOrDefault(), y.GetValueOrDefault()) != 0);
bool? VB$LW$t_struct$S1 = (x.HasValue & y.HasValue) ? VB$LW$t_struct$S3 : null;
if (VB$LW$t_struct$S1.GetValueOrDefault())
{
Console.WriteLine("true");
}
else
{
Console.WriteLine("false");
}
}
You'll see that the comparison in Visual Basic returns Nullable<bool> (not bool, false or true!). And undefined converted to bool is false.
Nothing compared to whatever is always Nothing, not false in Visual Basic (it is the same as in SQL).

The problem that's observed here is a special case of a more general problem, which is that the number of different definitions of equality that may be useful in at least some circumstances exceeds the number of commonly-available means to express them. This problem is in some cases made worse by an unfortunate belief that it is confusing to have different means of testing equality yield different results, and such confusion might be avoided by having the different forms of equality yield the same results whenever possible.
In reality, the fundamental cause of confusion is a misguided belief that the different forms of equality and inequality testing should be expected to yield the same result, notwithstanding the fact that different semantics are useful in different circumstances. For example, from an arithmetic standpoint, it's useful to be able to have Decimal which differ only in the number of trailing zeroes compare as equal. Likewise for double values like positive zero and negative zero. On the other hand, from a caching or interning standpoint, such semantics can be deadly. Suppose, for example, one had a Dictionary<Decimal, String> such that myDict[someDecimal] should equal someDecimal.ToString(). Such an object would seem reasonable if one had many Decimal values that one wanted to convert to string and expected there to be many duplicates. Unfortunately, if used such caching to convert 12.3 m and 12.40 m, followed by 12.30 m and 12.4 m, the latter values would yield "12.3", and "12.40" instead of "12.30" and "12.4".
Returning to the matter at hand, there is more than one sensible way of comparing nullable objects for equality. C# takes the standpoint that its == operator should mirror the behavior of Equals. VB.NET takes the standpoint that its behavior should mirror that of some other languages, since anyone who wants the Equals behavior could use Equals. In some sense, the right solution would be to have a three-way "if" construct, and require that if the conditional expression returns a three-valued result, code must specify what should happen in the null case. Since that is not an option with languages as they are, the next best alternative is to simply learn how different languages work and recognize that they are not the same.
Incidentally, Visual Basic's "Is" operator, which is lacking in C, can be used to test for whether a nullable object is, in fact, null. While one might reasonably question whether an if test should accept a Boolean?, having the normal comparison operators return Boolean? rather than Boolean when invoked on nullable types is a useful feature. Incidentally, in VB.NET, if one attempts to use the equality operator rather than Is, one will get a warning that the result of the comparison will always be Nothing, and one should use Is if one wants to test if something is null.

May be
this
post well help you:
If I remember correctly, 'Nothing' in VB means "the default value". For a value type, that's the default value, for a reference type, that would be null. Thus, assigning nothing to a struct, is no problem at all.

This is a definite weirdness of VB.
In VB, if you want to compare two nullable types, you should use Nullable.Equals().
In your example, it should be:
Dim x As System.Nullable(Of Decimal) = Nothing
Dim y As System.Nullable(Of Decimal) = Nothing
y = 5
If Not Nullable.Equals(x, y) Then
Console.WriteLine("true")
Else
Console.WriteLine("false")
End If

Your VB code is simply incorrect - if you change the "x <> y" to "x = y" you will still have "false" as the result. The most common way of expression this for nullable instances is "Not x.Equals(y)", and this will yield the same behavior as "x != y" in C#.

Related

Why does VB convert true into -1 not 1? [duplicate]

I am working with some VB.NET code that seems to be casting a boolean value to an integer using CInt(myBoolean). The odd thing that is happening is that it returns -1 if the value is true. For example:
CInt(True) // returns -1
CInt(False) // returns 0
Is this common in other languages?
I thought that a boolean would be 1 if true and 0 if false. Also, is there a way to make Visual Basic assign 1 to true instead of assigning -1?
Typically, a value of false is represented by 0 and a value of true is represented by any non-0 integer value. The specific value for true and false (among others) are things that you shouldn't rely on - they can potentially be implementation specific. I'm not sure what you are trying to do, but it would probably be best to not rely on True or False having any specific integer values unless you absolutely have to.
The best explanation that I could find for VB's specific behavior comes from Wikipedia:
Boolean constant True has numeric value −1. This is because the Boolean data type is stored as a 16-bit signed integer. In this construct −1 evaluates to 16 binary 1s (the Boolean value True), and 0 as 16 0s (the Boolean value False). This is apparent when performing a Not operation on a 16 bit signed integer value 0 which will return the integer value −1, in other words True = Not False. This inherent functionality becomes especially useful when performing logical operations on the individual bits of an integer such as And, Or, Xor and Not.[4] This definition of True is also consistent with BASIC since the early 1970s Microsoft BASIC implementation and is also related to the characteristics of CPU instructions at the time.
A work around for your initial use would be :
Dim i As Integer = CInt(Int(False))
This will return a 0.
Dim i As Integer = CInt(Int(True))
This will return a 1.
It seems like a gotcha, and I don't know any other examples of this behaviour.
Troubleshooting Data Types (Visual Basic) specifies this behaviour, with a "Don't do that, mkay" sorta remark with it. Do note further down:
Conversion in the Framework
The ToInt32 method of the Convert class in the System namespace converts True to +1.
If you must convert a Boolean value to a numeric data type, be careful about which conversion method you use.
I had the same problem and used Math.Abs function on the result :)
The MSDN documentation provides some valuable insight, "Boolean values are not stored as numbers, and the stored values are not intended to be equivalent to numbers. You should never write code that relies on equivalent numeric values for True and False. Whenever possible, you should restrict usage of Boolean variables to the logical values for which they are designed."
Many versions of BASIC in the 1970's and 1980's implemented bit-wise arithmetic with their AND and OR operators, and made true conditional expressions evaluate to -1 (i.e. the "all-bits-set" value in the two's complement used for integers). I'm not sure exactly why the decision was made to have true conditional expressions evaluate to an all-bits-set value; being able to use AND to mask an integer against a conditional expression may have been faster than multiplying, but given then internal mechanics of the interpreters the difference would have been slight.
In any case, the first versions of BASIC that Microsoft produced for the PC followed in that tradition of having true conditionals evaluate to -1 (all-bits-set); since QuickBASIC was in turn supposed to be compatible with those, and Visual Basic was supposed to be compatible with QuickBASIC, they used the same representation. Although .Net recognizes integers and Booleans as different types, VB.Net wanted to offer a migration path for VB6 programs that might rely on the old behavior. With "Option Strict Off", VB.Net will implicitly convert a Boolean value of True to an integer -1; while most programmers use Option Strict On, it would be confusing to have the behavior of CInt() differ from the implicit conversion behavior.
I tested it and got the following results:
Public Module BooleanTest
Public Function GetTrue() As Boolean
GetTrue = True
End Function
End Module
...
[StructLayout(LayoutKind.Explicit)]
struct MyStruct
{
[FieldOffset(0)]
public bool MyBool;
[FieldOffset(0)]
public int MyInt32;
}
static void Main(string[] args)
{
MyStruct b1, b2;
b1.MyInt32 = 0;
b2.MyInt32 = 0;
b1.MyBool = BooleanTest.BooleanTest.GetTrue();
b2.MyBool = true;
Console.WriteLine(b1.MyInt32);
Console.WriteLine(b2.MyInt32);
}
This will result in:
1
1
I hope this proves that all True values inside .NET are always the same. The reason is simple: All .NET members have to communicatie with each other. It would be weird if object.Equals(trueFromCSharp, trueFromVB) would result in false (as will trueFromCSharp == trueFromVB).
CInt is just a function which will convert True into -1. Another function Int will return 1. But these are converters, and do not say anything about the binary values.
I have been having the same problem with MySQL as this has no Boolean type only a tinyint(1).
My solution was to write a converter function to ensure that the values are correct before inserting them into the database
Public Function BoolToMySql(bVal As Boolean) As Integer
Dim retVal As Integer
If bVal = True Then
retVal = 1
Else
retVal = 0
End If
BoolToMySql = retVal
End Function
I hope that is can help others work with Booleans inside VB.NET.
Just as a better way to write the VB.NET that Roger wrote:
Public Function BoolToMySql(bVal As Boolean) As Integer
return If(bVal, 1, 0)
End Function
I found the other answers lacking for the specific VBA scenario I was working with. This is not tested in VB.NET.
I wanted to take any given number that was <> 0 and make it 1, and keep 0 as 0 in a single line of code, without an If statement. The way that I ended up doing this, which I did not see in other given answers, was:
Abs(CBool(iCount))
CBool() converts the given number (iCount in example above) to Boolean, narrowing the possible results to two values; True with a value of -1 and False with a value of 0.
Abs() then takes the absolute value (no negatives) of the Boolean to return 0 for False and 1 for True.
In practice, the following return 0:
Abs(CBool(0))
Abs(False)
And the following return 1:
Abs(CBool(1))
Abs(CBool(-1))
Abs(CBool(-38473))
Abs(CBool(358677))
Abs(True)
I hope that this is useful for anyone else playing with specific scenarios such as this.
I may be a bit late but here's a simple workaround to receive the typical 1 for True and 0 for False.
Multiply the boolean with -1 like so:
CInt(True) * -1
CInt(False) * -1
Which then returns
1
0

What are the implicit type conversion rules in vba?

I know the following is possible in vba:
If Len(str) Then
Len(str) will evaluate to false if Len(str) is 0, true otherwise.
What are the exact conversion rules for vba? Please provide a link to official documentation if possible.
A boolean variable can have only two states, True and False. That's true in all programming languages (except if a nullable datatype exists, eg bool? in C# where the variable can have also the value null signaling it is not defined).
In VBA, a boolean is stored as 16-bit integer. False is defined as 0 and True as -1. Thats similar in other languages, simply because 0 has all bits set to 0 and -1 has all bits set to 1. But this is an implementation detail and your programming should not care about it. You deal with True and False and Boolean algebra using AND, OR and NOT.
Implicit conversion rules are straight forward: Numeric data types are converted to boolean False if they are 0 and to True in all other cases (that is, if the value has any bit set to 1). Converting a boolean value to a number results in 0 resp. -1.
Dim i As Integer
Dim b As Boolean
i = 3
b = i ' Implicit conversion from 3 to TRUE
i = b ' Implicit conversion from TRUE to -1
Strings are converted to numbers first, and if that fails, you get a runtime error. Dates are implemented as numbers (Double) in VBA, so every date/time is True except the date+time that is represented as 0 (that is 30 Dec 1899 00:00)
But from my point of view (and >30 years of programming), implicit conversions are evil and should be avoided. They are source of numerous errors, they lead to code that is harder to read - and there is simply no reason to rely on implicit conversations. There is no reason to write If len(str) Then. You want to check if the length of a string is greater than 0, so write it: If len(str) > 0 Then. Always.
Have a look to the following example: Obviously, the VBA runtime need to execute an implicit conversion, but can you tell the outcome?
Dim i As Integer
Dim b As Boolean
i = 3
b = i
If b = i Then Debug.Print "B=I"
Here you can find the official documentation from Microsoft: https://learn.microsoft.com/en-US/office/vba/language/reference/user-interface-help/boolean-data-type
I came across this in a recent textbook I was reading on C++ - I'll add a link at the end. Although a different language, both have similarities to some degree. One of these similarities is what you call 'implicit type conversion' or more technically, type casting.
For any Boolean expression in your project when you compile your VBA project (Debug > Compile VBAProject) any nonzero value will be treated as the value true and will treat the value 0 as false. Essentially this comes down to the how the compiler treats these statesments and also to operator precedence rules as well as boolean expression rules.
For further reading please see below links:
Operator precedence
Boolean data type
In C++ (as far as I am aware) this term is called copy initialisation, you can read more here:
Copy initialization
The book that brought this deep thought recently:
Problem Solving with C++: The Object of Programming
I know this doesn't necessarily answer your question, but hopefully some of the references help.

How does If 1 = 2 = 3 Then (...) work

While coding in VB.NET i accidently wrote something like this into my code;
Dim a as integer = 1
Dim b as Integer = 2
Dim c as Integer = 3
If a = b = c Then
'.. some Code
End if
It did not give me an error so it looks like it is a part of the programming language (or programming itself) that I do not know.
My Question:
How does this statement work and what are good examples for a use-case of this code?
Edit:
Dim a as Integer = 1 = 2 = 3
This line also does not give me an error. MsgBox(a) shows me a 0
The Answer:
You can find it in the questions comments.
Yes, with Option Strict Off in effect (the default) that is a valid expression. In that mode, VB.NET behaves (almost) like a dynamic language in the spirit of Javascript, Python or Ruby. The compiler emits conversions where needed to make the expression valid, readily converting between strings, booleans and numeric types.
The expression is evaluated left-to-right, in other words (a = b) = c. The a = b subexpression generates Boolean, it needs to be converted to Integer to make the comparison with c valid. False produces 0, True produces -1. Not a typo btw, part of the legacy when VB was still heavily based on COM automation.
The final expression type is Boolean again. Note how your second statement requires that to be converted to Integer. Since a = b with the given values always produces False, 0 = c will always be False.
With Option Strict On in effect, the compiler no longer applies these automatic conversions and generates an error on this statement. It insists you use CInt() to convert the Boolean sub-expression. Invariably good enough to discover that you fumbled the expression.
You probably ought to consider changing the default if mishaps like this byte frequently. Use Tools > Options > Projects and Solutions > VB Defaults > Option Strict setting. Beware that it will be hard to ever switch back :)

When does = perform comparison instead of assignment?

In VB.NET, there's no == operator for comparison, so the = operator serves that purpose as well as assignment. I have a function, and I want it to return the boolean result of a comparison, without storing that result in a variable:
Private Function foo() As Boolean
Dim bar As Integer = 1
Return bar = 2
End Function
Returns: False
OK, but what's the value of bar?
Private Function foo() As KeyValuePair(Of Boolean, Integer)
Dim bar As Integer = 1
Return New KeyValuePair(Of Boolean, Integer)(bar = 2, bar)
End Function
Returns: False, 1
It looks like = will perform a comparison when the statement context demands it, but is this guaranteed? That is, can I be sure that bar will never be set to 2 in this situation?
Also, I know that VB.NET doesn't allow chained inline assignments, which may be for the best. Does this odd = behavior cause any other quirks I should be aware of?
You cannot do in-line assignments in VB, Assignment is an explicit statement:
[Let] <<target-reference>> = <<value-expression>>
The Let is optional and implicit, and hardly ever used anymore. The general rule that you can use to distinguish the [Let] command from equality testing is that for Let, no other keyword may come before the target-reference in the statement. AFAIK, in all cases of = as equality testing, there is one or more other keywords that precede it in the statement.
In your first example, the keyword Return precedes your =, so it's an equality test, and not an assignment.
In your first example you can do either:
Return 2
or
bar = 2
Return bar
As for your question "OK, but what's the value of bar?", bar still equals one.
= in VB cause no quirks. It works exactly as documented, and it always has (including its predecessor, BASIC back to 1968).
If you are starting to code in VB (coming from a language like C#), you should start getting used to the peculiar VB way of doing things; which is based on the idea: as simple and intuitive for the programmer as possible. "If assignation and comparison happen always in different contexts, why not using the same operator and let the context define its exact meaning?" -> VB-way of seeing things. "No, different realities have to be accounted for by different operators. End of the discussion" -> C#-way. :)
Is this reliable? Can you blindly trust on these not-always-clear-for-a-programmer bits? Sure, VB.NET peculiarities are highly-reliable and trustworthy. You can always use = (or Is on some contexts, but VS would tell you) and be completely sure that the code will do what is expected. But the question is: are you sure that you write exactly what you want?
This last question is what, perhaps, is more criticable of VB and what might give some problems to programmers from other languages: the higher the flexibility, the more likely is that you make an error; mainly if you are used to a different format.
Regarding the chained inline assignments, I honestly don't see its true utility (and never use them in C#). Regarding other differences with respect to C#, there are plenty of them; in some cases, I think that the C# approach is better; other times, the VB.NET one. On readability/length of code, I can refer to the With Statement I have always found somehow useful which is not present in C#.
One way to have 100% sure that the expression will be evaluated as an boolean expression is to use ()
e.g
Dim a = 2
Return (a = 1)
Since you cannot set a value to a variable wihtin the parenthesis.
What i want to say is: on an return statament for example you cant assing a value to a variable so, even if you use
a = 1
The compilator knows that this expression only can be an boolean expression.
The same to the if statament and so on..
Heh back in QB45 days we used to exploit the fact that "True" was the numeric value -1. So you would see code like x = 1 - x * (x < 6) (translation: increment x, but reset to 1 when it gets to 6)

In how many languages is Null not equal to anything not even Null?

In how many languages is Null not equal to anything not even Null?
It's this way in SQL (as a logic language) because null means unknown/undefined.
However, in programming languages (like say, C++ or C#), a null pointer/reference is a specific value with a specific meaning -- nothing.
Two nothings are equivilent, but two unknowns are not. The confusion comes from the fact that the same name (null) is used for both concepts.
In VB6 the expression Null = Null will produce Null instead of True as you would expect.
This will cause a runtime error if you try to assign it to a Boolean, however if you use it
as the condition of "If ... Then" it will act like False. Moreover Null <> Null will also
produce Null, so:
In VB6 you could say that Null is neither equal to itself (or anything else), nor unequal!
You're supposed to test for it using the IsNull() function.
VB6 also has other special values:
Nothing for object references. Nothing = Nothing is a compile error. (you're supposed to compare it using "is")
Missing for optional parameters which haven't been given. It has no literal representation so you can't even write Missing = Missing. (the test is IsMissing(foo))
Empty for uninitialized variables. This one does test equal to itself although there's also a function IsEmpty().
... let me know if I've forgotten one
I remember being a bit disgusted with VB.
Oracle is this way.
SELECT * FROM dual WHERE NULL=null; --no rows returned
MySQL has a null-safe equality operator, <=>, which returns true if both sides are equal or both sides are null. See MySQL Docs.
In C#, Nullable<bool> has interesting properties with respect to logical operators, but the equality operator is the same as other types in that language (i.e., ((bool?)null == (bool?)null) == true).
To preserve the short-circuited behavior of the short-circuited logical operators, and to preserve consistency with the non-short-circuited logical operators, the nullable boolean has some interesting properties. For example: true || null == true. false && null == false, etc. This stands in direct contradiction with other three-valued logic languages such as ANSI SQL.
You can make ruby work that way:
class Null
def self.==(other);false;end
end
n=Null
print "Null equals nothing" if n!=Null
In SQL you would have to do something like:
WHERE column is NULL
rather than
WHERE column = NULL