Empty string comparisons

  • Thread starter Thread starter Neville Lang
  • Start date Start date
Göran,
What do you mean? The "=" operator is only used for equality, not
identity.
Read the statement again, I was referring to C#:

If you don't override "==" in C# it is used for identity (it reverts to the
IL equevilent of Object.ReferenceEquals); if you do override "=" then it
would (hopefully) be used for equality.

public class Class1
{
}


Class1 c1 = new Class1(), c2 = c1;
Debug.WriteLine(c1 == c2);
 
Jay said:
Göran,
Read the statement again, I was referring to C#:

If you don't override "==" in C# it is used for identity (it reverts to
the IL equevilent of Object.ReferenceEquals); if you do override "="
then it would (hopefully) be used for equality.

public class Class1
{
}


Class1 c1 = new Class1(), c2 = c1;
Debug.WriteLine(c1 == c2);

Of course it does. If you don't override the operator, it's inherited,
and that is the way that you compare two objects.

If you don't like it that way, you can always do it exactly the same way
VB does:

Debug.WriteLine(Microsoft.VisualBasic.CompilerServices.Operators.CompareObjectEqual(c1,c2));

The method is implemented as:

======

Public Shared Function CompareObjectEqual(ByVal Left As Object, ByVal
Right As Object, ByVal TextCompare As Boolean) As Object
Dim class1 As CompareClass = Operators.CompareObject2(Left,
Right, TextCompare)
Select Case class1
Case CompareClass.Unordered
Return False
Case CompareClass.UserDefined
Return
Operators.InvokeUserDefinedOperator(UserDefinedOperator.Equal, New
Object() { Left, Right })
Case CompareClass.Undefined
Throw
Operators.GetNoValidOperatorException(UserDefinedOperator.Equal, Left,
Right)
End Select
Return (class1 = CompareClass.Equal)
End Function

======

The real work is done in the CompareObject2 method, which is implemented as:

======

Private Shared Function CompareObject2(ByVal Left As Object, ByVal Right
As Object, ByVal TextCompare As Boolean) As CompareClass
Dim code1 As TypeCode
Dim code2 As TypeCode
Dim convertible1 As IConvertible = TryCast(Left,IConvertible)
If (convertible1 Is Nothing) Then
If (Left Is Nothing) Then
code1 = TypeCode.Empty
Else
code1 = TypeCode.Object
End If
Else
code1 = convertible1.GetTypeCode
End If
Dim convertible2 As IConvertible = TryCast(Right,IConvertible)
If (convertible2 Is Nothing) Then
If (Right Is Nothing) Then
code2 = TypeCode.Empty
Else
code2 = TypeCode.Object
End If
Else
code2 = convertible2.GetTypeCode
End If
If (code1 = TypeCode.Object) Then
Dim chArray1 As Char() = TryCast(Left,Char())
If ((Not chArray1 Is Nothing) AndAlso (((code2 =
TypeCode.String) OrElse (code2 = TypeCode.Empty)) OrElse ((code2 =
TypeCode.Object) AndAlso TypeOf Right Is Char()))) Then
Left = New String(chArray1)
convertible1 = DirectCast(Left, IConvertible)
code1 = TypeCode.String
End If
End If
If (code2 = TypeCode.Object) Then
Dim chArray2 As Char() = TryCast(Right,Char())
If ((Not chArray2 Is Nothing) AndAlso ((code1 =
TypeCode.String) OrElse (code1 = TypeCode.Empty))) Then
Right = New String(chArray2)
convertible2 = DirectCast(Right, IConvertible)
code2 = TypeCode.String
End If
End If
Select Case ((code1 * (TypeCode.String Or TypeCode.Object)) + code2)
Case TypeCode.Empty
Return CompareClass.Equal
Case TypeCode.Boolean
Return Operators.CompareBoolean(False,
convertible2.ToBoolean(Nothing))
Case TypeCode.Char
Return Operators.CompareChar(ChrW(0),
convertible2.ToChar(Nothing))
Case TypeCode.SByte
Return Operators.CompareInt32(0,
convertible2.ToSByte(Nothing))
Case TypeCode.Byte
Return Operators.CompareInt32(0,
convertible2.ToByte(Nothing))
Case TypeCode.Int16
Return Operators.CompareInt32(0,
convertible2.ToInt16(Nothing))
Case TypeCode.UInt16
Return Operators.CompareInt32(0,
convertible2.ToUInt16(Nothing))
Case TypeCode.Int32
Return Operators.CompareInt32(0,
convertible2.ToInt32(Nothing))
Case TypeCode.UInt32
Return Operators.CompareUInt32(0,
convertible2.ToUInt32(Nothing))
Case TypeCode.Int64
Return Operators.CompareInt64(CLng(0),
convertible2.ToInt64(Nothing))
Case TypeCode.UInt64
Return Operators.CompareUInt64(0,
convertible2.ToUInt64(Nothing))
Case TypeCode.Single
Return Operators.CompareSingle(0!,
convertible2.ToSingle(Nothing))
Case TypeCode.Double
Return Operators.CompareDouble(0,
convertible2.ToDouble(Nothing))
Case TypeCode.Decimal
Return
Operators.CompareDecimal(DirectCast(Decimal.Zero, IConvertible),
convertible2)
Case TypeCode.DateTime
Return Operators.CompareDate(DateTime.MinValue,
convertible2.ToDateTime(Nothing))
Case TypeCode.String
Return DirectCast(Operators.CompareString(Nothing,
convertible2.ToString(Nothing), TextCompare), CompareClass)
Case DirectCast(57, TypeCode)
Return
Operators.CompareBoolean(convertible1.ToBoolean(Nothing), False)
Case DirectCast(60, TypeCode)
Return
Operators.CompareBoolean(convertible1.ToBoolean(Nothing),
convertible2.ToBoolean(Nothing))
Case DirectCast(62, TypeCode)
Return
Operators.CompareInt32(Operators.ToVBBool(convertible1),
convertible2.ToSByte(Nothing))
Case DirectCast(63, TypeCode), DirectCast(64, TypeCode)
Return
Operators.CompareInt32(Operators.ToVBBool(convertible1),
convertible2.ToInt16(Nothing))
Case DirectCast(65, TypeCode), DirectCast(66, TypeCode)
Return
Operators.CompareInt32(Operators.ToVBBool(convertible1),
convertible2.ToInt32(Nothing))
Case DirectCast(67, TypeCode), DirectCast(68, TypeCode)
Return
Operators.CompareInt64(CLng(Operators.ToVBBool(convertible1)),
convertible2.ToInt64(Nothing))
Case DirectCast(69, TypeCode), DirectCast(72, TypeCode)
Return
Operators.CompareDecimal(Operators.ToVBBoolConv(convertible1), convertible2)
Case DirectCast(70, TypeCode)
Return
Operators.CompareSingle(CSng(Operators.ToVBBool(convertible1)),
convertible2.ToSingle(Nothing))
Case DirectCast(71, TypeCode)
Return
Operators.CompareDouble(CDbl(Operators.ToVBBool(convertible1)),
convertible2.ToDouble(Nothing))
Case DirectCast(75, TypeCode)
Return
Operators.CompareBoolean(convertible1.ToBoolean(Nothing),
Conversions.ToBoolean(convertible2.ToString(Nothing)))
Case DirectCast(76, TypeCode)
Return
Operators.CompareChar(convertible1.ToChar(Nothing), ChrW(0))
Case DirectCast(80, TypeCode)
Return
Operators.CompareChar(convertible1.ToChar(Nothing),
convertible2.ToChar(Nothing))
Case DirectCast(94, TypeCode), DirectCast(346, TypeCode),
DirectCast(360, TypeCode)
Return
DirectCast(Operators.CompareString(convertible1.ToString(Nothing),
convertible2.ToString(Nothing), TextCompare), CompareClass)
Case DirectCast(95, TypeCode)
Return
Operators.CompareInt32(convertible1.ToSByte(Nothing), 0)
Case DirectCast(98, TypeCode)
Return
Operators.CompareInt32(convertible1.ToSByte(Nothing),
Operators.ToVBBool(convertible2))
Case DirectCast(100, TypeCode)
Return
Operators.CompareInt32(convertible1.ToSByte(Nothing),
convertible2.ToSByte(Nothing))
Case DirectCast(101, TypeCode), DirectCast(102, TypeCode),
DirectCast(119, TypeCode), DirectCast(121, TypeCode), DirectCast(138,
TypeCode), DirectCast(139, TypeCode), DirectCast(140, TypeCode)
Return
Operators.CompareInt32(convertible1.ToInt16(Nothing),
convertible2.ToInt16(Nothing))
Case DirectCast(103, TypeCode), DirectCast(104, TypeCode),
DirectCast(123, TypeCode), DirectCast(141, TypeCode), DirectCast(142,
TypeCode), DirectCast(157, TypeCode), DirectCast(159, TypeCode),
DirectCast(161, TypeCode), DirectCast(176, TypeCode), DirectCast(177,
TypeCode), DirectCast(178, TypeCode), DirectCast(179, TypeCode),
DirectCast(180, TypeCode)
Return
Operators.CompareInt32(convertible1.ToInt32(Nothing),
convertible2.ToInt32(Nothing))
Case DirectCast(105, TypeCode), DirectCast(106, TypeCode),
DirectCast(125, TypeCode), DirectCast(143, TypeCode), DirectCast(144,
TypeCode), DirectCast(163, TypeCode), DirectCast(181, TypeCode),
DirectCast(182, TypeCode), DirectCast(195, TypeCode), DirectCast(197,
TypeCode), DirectCast(199, TypeCode), DirectCast(201, TypeCode),
DirectCast(214, TypeCode), DirectCast(215, TypeCode), DirectCast(216,
TypeCode), DirectCast(217, TypeCode), DirectCast(218, TypeCode),
DirectCast(219, TypeCode), DirectCast(220, TypeCode)
Return
Operators.CompareInt64(convertible1.ToInt64(Nothing),
convertible2.ToInt64(Nothing))
Case DirectCast(107, TypeCode), DirectCast(110, TypeCode),
DirectCast(129, TypeCode), DirectCast(145, TypeCode), DirectCast(148,
TypeCode), DirectCast(167, TypeCode), DirectCast(183, TypeCode),
DirectCast(186, TypeCode), DirectCast(205, TypeCode), DirectCast(221,
TypeCode), DirectCast(224, TypeCode), DirectCast(233, TypeCode),
DirectCast(235, TypeCode), DirectCast(237, TypeCode), DirectCast(239,
TypeCode), DirectCast(243, TypeCode), DirectCast(290, TypeCode),
DirectCast(291, TypeCode), DirectCast(292, TypeCode), DirectCast(293,
TypeCode), DirectCast(294, TypeCode), DirectCast(295, TypeCode),
DirectCast(296, TypeCode), DirectCast(297, TypeCode), DirectCast(300,
TypeCode)
Return Operators.CompareDecimal(convertible1,
convertible2)
Case DirectCast(108, TypeCode), DirectCast(127, TypeCode),
DirectCast(146, TypeCode), DirectCast(165, TypeCode), DirectCast(184,
TypeCode), DirectCast(203, TypeCode), DirectCast(222, TypeCode),
DirectCast(241, TypeCode), DirectCast(252, TypeCode), DirectCast(253,
TypeCode), DirectCast(254, TypeCode), DirectCast(255, TypeCode),
DirectCast(256, TypeCode), DirectCast(257, TypeCode), DirectCast(258,
TypeCode), DirectCast(259, TypeCode), DirectCast(260, TypeCode),
DirectCast(262, TypeCode), DirectCast(298, TypeCode)
Return
Operators.CompareSingle(convertible1.ToSingle(Nothing),
convertible2.ToSingle(Nothing))
Case DirectCast(109, TypeCode), DirectCast(128, TypeCode),
DirectCast(147, TypeCode), DirectCast(166, TypeCode), DirectCast(185,
TypeCode), DirectCast(204, TypeCode), DirectCast(223, TypeCode),
DirectCast(242, TypeCode), DirectCast(261, TypeCode), DirectCast(271,
TypeCode), DirectCast(272, TypeCode), DirectCast(273, TypeCode),
DirectCast(274, TypeCode), DirectCast(275, TypeCode), DirectCast(276,
TypeCode), DirectCast(277, TypeCode), DirectCast(278, TypeCode),
DirectCast(279, TypeCode), DirectCast(280, TypeCode), DirectCast(281,
TypeCode), DirectCast(299, TypeCode)
Return
Operators.CompareDouble(convertible1.ToDouble(Nothing),
convertible2.ToDouble(Nothing))
Case DirectCast(113, TypeCode), DirectCast(132, TypeCode),
DirectCast(151, TypeCode), DirectCast(170, TypeCode), DirectCast(189,
TypeCode), DirectCast(208, TypeCode), DirectCast(227, TypeCode),
DirectCast(246, TypeCode), DirectCast(265, TypeCode), DirectCast(284,
TypeCode), DirectCast(303, TypeCode)
Return
Operators.CompareDouble(convertible1.ToDouble(Nothing),
Conversions.ToDouble(convertible2.ToString(Nothing)))
Case DirectCast(114, TypeCode)
Return
Operators.CompareInt32(convertible1.ToByte(Nothing), 0)
Case DirectCast(117, TypeCode)
Return
Operators.CompareInt32(convertible1.ToInt16(Nothing),
Operators.ToVBBool(convertible2))
Case DirectCast(120, TypeCode)
Return
Operators.CompareInt32(convertible1.ToByte(Nothing),
convertible2.ToByte(Nothing))
Case DirectCast(122, TypeCode), DirectCast(158, TypeCode),
DirectCast(160, TypeCode)
Return
Operators.CompareInt32(convertible1.ToUInt16(Nothing),
convertible2.ToUInt16(Nothing))
Case DirectCast(124, TypeCode), DirectCast(162, TypeCode),
DirectCast(196, TypeCode), DirectCast(198, TypeCode), DirectCast(200,
TypeCode)
Return
Operators.CompareUInt32(convertible1.ToUInt32(Nothing),
convertible2.ToUInt32(Nothing))
Case DirectCast(126, TypeCode), DirectCast(164, TypeCode),
DirectCast(202, TypeCode), DirectCast(234, TypeCode), DirectCast(236,
TypeCode), DirectCast(238, TypeCode), DirectCast(240, TypeCode)
Return
Operators.CompareUInt64(convertible1.ToUInt64(Nothing),
convertible2.ToUInt64(Nothing))
Case DirectCast(133, TypeCode)
Return
Operators.CompareInt32(convertible1.ToInt16(Nothing), 0)
Case DirectCast(136, TypeCode)
Return
Operators.CompareInt32(convertible1.ToInt16(Nothing),
Operators.ToVBBool(convertible2))
Case DirectCast(152, TypeCode)
Return
Operators.CompareInt32(convertible1.ToUInt16(Nothing), 0)
Case DirectCast(155, TypeCode)
Return
Operators.CompareInt32(convertible1.ToInt32(Nothing),
Operators.ToVBBool(convertible2))
Case DirectCast(171, TypeCode)
Return
Operators.CompareInt32(convertible1.ToInt32(Nothing), 0)
Case DirectCast(174, TypeCode)
Return
Operators.CompareInt32(convertible1.ToInt32(Nothing),
Operators.ToVBBool(convertible2))
Case DirectCast(190, TypeCode)
Return
Operators.CompareUInt32(convertible1.ToUInt32(Nothing), 0)
Case DirectCast(193, TypeCode)
Return
Operators.CompareInt64(convertible1.ToInt64(Nothing),
CLng(Operators.ToVBBool(convertible2)))
Case DirectCast(209, TypeCode)
Return
Operators.CompareInt64(convertible1.ToInt64(Nothing), CLng(0))
Case DirectCast(212, TypeCode)
Return
Operators.CompareInt64(convertible1.ToInt64(Nothing),
CLng(Operators.ToVBBool(convertible2)))
Case DirectCast(228, TypeCode)
Return
Operators.CompareUInt64(convertible1.ToUInt64(Nothing), 0)
Case DirectCast(231, TypeCode)
Return Operators.CompareDecimal(convertible1,
Operators.ToVBBoolConv(convertible2))
Case DirectCast(247, TypeCode)
Return
Operators.CompareSingle(convertible1.ToSingle(Nothing), 0!)
Case DirectCast(250, TypeCode)
Return
Operators.CompareSingle(convertible1.ToSingle(Nothing),
CSng(Operators.ToVBBool(convertible2)))
Case DirectCast(266, TypeCode)
Return
Operators.CompareDouble(convertible1.ToDouble(Nothing), 0)
Case DirectCast(269, TypeCode)
Return
Operators.CompareDouble(convertible1.ToDouble(Nothing),
CDbl(Operators.ToVBBool(convertible2)))
Case DirectCast(285, TypeCode)
Return Operators.CompareDecimal(convertible1,
DirectCast(Decimal.Zero, IConvertible))
Case DirectCast(288, TypeCode)
Return Operators.CompareDecimal(convertible1,
Operators.ToVBBoolConv(convertible2))
Case DirectCast(304, TypeCode)
Return
Operators.CompareDate(convertible1.ToDateTime(Nothing), DateTime.MinValue)
Case DirectCast(320, TypeCode)
Return
Operators.CompareDate(convertible1.ToDateTime(Nothing),
convertible2.ToDateTime(Nothing))
Case DirectCast(322, TypeCode)
Return
Operators.CompareDate(convertible1.ToDateTime(Nothing),
Conversions.ToDate(convertible2.ToString(Nothing)))
Case DirectCast(342, TypeCode)
Return
DirectCast(Operators.CompareString(convertible1.ToString(Nothing),
Nothing, TextCompare), CompareClass)
Case DirectCast(345, TypeCode)
Return
Operators.CompareBoolean(Conversions.ToBoolean(convertible1.ToString(Nothing)),
convertible2.ToBoolean(Nothing))
Case DirectCast(347, TypeCode), DirectCast(348, TypeCode),
DirectCast(349, TypeCode), DirectCast(350, TypeCode), DirectCast(351,
TypeCode), DirectCast(352, TypeCode), DirectCast(353, TypeCode),
DirectCast(354, TypeCode), DirectCast(355, TypeCode), DirectCast(356,
TypeCode), DirectCast(357, TypeCode)
Return
Operators.CompareDouble(Conversions.ToDouble(convertible1.ToString(Nothing)),
convertible2.ToDouble(Nothing))
Case DirectCast(358, TypeCode)
Return
Operators.CompareDate(Conversions.ToDate(convertible1.ToString(Nothing)),
convertible2.ToDateTime(Nothing))
End Select
If ((code1 <> TypeCode.Object) AndAlso (code2 <>
TypeCode.Object)) Then
Return CompareClass.Undefined
End If
Return CompareClass.UserDefined
End Function

======

Yeah, that is much better. ;)
 
Göran,
Of course it does. If you don't override the operator, it's inherited, and
that is the way that you compare two objects.
"Of course it does" what? Are you now agreeing with me that C# uses the "=="
operator for both equality & identity? Unlike VB that has separate operators
for equality & identity?
If you don't like it that way, you can always do it exactly the same way
VB does:
As I stated earlier I can use "==" for (blurred) equality/identity in C# or
use Object.ReferenceEquals if I explicitly need identity in C#. I really
don't see the connection of a VB equality routine will help with Identity in
C#!
 
Jay said:
Göran,
"Of course it does" what? Are you now agreeing with me that C# uses the
"==" operator for both equality & identity? Unlike VB that has separate
operators for equality & identity?

The operator is used for equality. For the Object class it's the
references that are compared, which gives that it works the same as
comparing identity.
As I stated earlier I can use "==" for (blurred) equality/identity in C#
or use Object.ReferenceEquals if I explicitly need identity in C#. I
really don't see the connection of a VB equality routine will help with
Identity in C#!

Maybe I wasn't clear enough; that's the method that is called when you
use the "=" operator in VB.
 
Göran,
which gives that it works the same as comparing identity.
It sounds like we're quibbling over semantics. ;-)

If it looks like a rose, smells like a rose, tastes like a rose, feels like
a rose; I would say it must be a rose!

If it "works the same as comparing identity", would that not be the same as
stating is it identity? It does in my book! ;-)
 
Jay said:
Göran,
It sounds like we're quibbling over semantics. ;-)

If it looks like a rose, smells like a rose, tastes like a rose, feels
like a rose; I would say it must be a rose!

If it "works the same as comparing identity", would that not be the same
as stating is it identity? It does in my book! ;-)

Yes, it's semantics, that's why it isn't a rose just because it happens
to look like a rose from a certain angle. ;)

Just because it works like identity doesn't mean that it is identity.

If you have two integer that have the value two, and multiply them, you
get the result four. If you add them together you also get the result
four. That however does not mean that multiplying and adding is the same
thing in this case. They are still completely different operations
eventhough they give the same result.

Anyway, the difference between VB.NET and C# isn't really how comparison
works, but what comparison is used. While the C# compiler actually uses
the operator supplied by the Object class to compare Object instances,
VB uses the
Microsoft.VisualBasic.CompilerServices.Operators.CompareObjectEqual
method instead.
 
Göran,
Yes, it's semantics, that's why it isn't a rose just because it happens to
look like a rose from a certain angle. ;)
OK, you haven't convinced me. :-|

Just because it works like identity doesn't mean that it is identity.
OK, reading the C# specification states:

<quote src="http://msdn2.microsoft.com/en-us/library/53k8ybth.aspx">
For predefined value types, the equality operator (==) returns true if the
values of its operands are equal, false otherwise. For reference types other
than string, == returns true if its two operands refer to the same object.
For the string type, == compares the values of the strings.
</quote>

Last I checked Identity equality means "two operands refer to the same
object" as their identity is the reference itself.

Anyway, the difference between VB.NET and C# isn't really how comparison
works, but what comparison is used.
Now we're getting someplace.

VB "IS" operator compiles to an ceq IL opcode for any reference type, not
just System.Object variables.

C# "==" operator compiles to the same ceq IL opcode for any reference type,
not just System.Object variables, unless of course that reference type
overloads the "==" operator.

As you may know you need VB 2005 to gain the use of overloaded operators. In
which case C#'s overloaded "==" operator is VB's overloaded "=" comparison
operator. Neither VB nor C# allows you to overload the assignment operator
itself. VB does not allow you to overload the IS operator.

While the C# compiler actually uses the operator supplied by the Object
class to compare Object instances, VB uses the
Microsoft.VisualBasic.CompilerServices.Operators.CompareObjectEqual
I'm sorry Göran; System.Object doesn't offer any operators!

As I stated earlier C# uses an IL opcode, the same opcode that is used (by
both VB & C#) for equality with types such as Int32, Byte, Double & other
"predefined value types".

Further C# uses the exactly same opcode for any reference type if you do not
overload the == operator! Such as my example of Class1! Compile and check
out the IL for the sample I gave earlier:

public class Class1
{
}


Class1 c1 = new Class1(), c2 = c1;
Debug.WriteLine(c1 == c2);

Try the same in VB, with Option Strict On and with Option Strict Off

Public Class Class1

End Class

Dim c1 As New Class1(), c2 = c1
Debug.WriteLine(c1 = c2)

Debug.WriteLine(c1 Is c2)

Try adding an overloaded equality operator in both C# & VB

public class Class1
{
public static bool operator ==(Class1 c1, Class1 c2)
{
}
public static bool operator !=(Class1 c1, Class1 c2)
{
}
}


Public Class Class1

Public Shared Operator =(ByVal c1 As Class1, ByVal c2 As Class1) As
Boolean

End Operator

Public Shared Operator <>(ByVal c1 As Class1, ByVal c2 As Class1) As
Boolean

End Operator

End Class

Compile both & examine the IL again.

Try the above for any number of reference types (Class), and any number of
value types (struct, Structure).

As far as I can tell CompareObjectEqual is only used when you have Option
Strict Off and only if both operands are System.Object types. If you have a
case where CompareObjectEqual is used with Option Strict On or used for
types other then System.Object I would love to see it!

To maximize compile errors over obscure runtime errors I normally use Option
Strict On. With Option Strict On the "=" comparison operator causes a
compile error in VB unless the type overloads the "=" operator. This means
that Int32, Byte, Double are allowed as are any types that VB implicitly
defines the operator for (such as Date) or the developer who defined the
type explicitly define the operator (such as TimeSpan). Some types, such as
String VB has defined its own equality operator that overrides String's =
operator. Option Strict On prohibits the "=" comparison operator on
reference types that do not overload the "=" comparison operator, this
includes System.Object!
 
Back
Top