|
Dit artikel is gepubliceerd op zondag 31 juli 2011 op vbvoorbeelden, bezoek de website voor een recente versie van dit artikel of andere artikels.
13.4.1. Logisch Gelijkheid tussen Value TypesNaast fysieke gelijkheid bestaat er ook nog zoiets als logische gelijkheid. De logica die gebruikt wordt om na te gaan of twee objecten logisch gelijk zijn is per type te bepalen.
Voor een aantal datatypes kan men de = vergelijkingsoperator gebruiken om logische gelijkheid na te gaan. Dit geldt voor de valuetypes, maar ook voor sommige ingebouwde referencetypes. Zo kan men bijvoorbeeld twee String objecten met de = operator op logische gelijkheid testen, bijvoorbeeld "abc" = "abc".
"Operator overloading" zou kunnen worden toegepast voor de datatypes die deze operator ( of andere operatoren ) niet ondersteunen, hierover in een later topic meer. Visual Basic 2010 Broncode Option Strict OnOption Explicit OnNamespace Example1 Class Client Public Shared Sub Main() Console.WriteLine(1 = 2) Console.WriteLine(1 <> 2) Dim string1 As New String( New Char() { "a"c, "b"c, "c"c}) Dim string2 As New String( New Char() { "a"c, "b"c, "c"c}) Console.WriteLine(string1 Is string2) Console.WriteLine(string1 = string2) Console.WriteLine(string1 <> string2) Console.ReadLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output False
True
True
False Operator <> kan gebruikt worden om na te gaan of er een verschil is tussen de twee operanden. Waarbij verschil betekent dat ze logisch gezien niet gelijk zijn aan elkaar. Elke datatype van expressies waarmee men via de = operator gelijkheid kan nagaan, ondersteunt ook de <> operator. Zie verderop voor meer informatie over operator overloading. boven
13.4.2. Equals MethodAlle valuetypes erven van de System.ValueType een Equals(obj As Object) As Boolean functie over.
Ook deze is bruikbaar om logisch gelijkheid na te gaan : Visual Basic 2010 Broncode Namespace Example2 Class Client Public Shared Sub Main() Console.WriteLine(1.Equals(2)) Dim string1 As New String( New Char() { "a"c, "b"c, "c"c}) Dim string2 As New String( New Char() { "a"c, "b"c, "c"c}) Console.WriteLine(string1.Equals(string2)) Console.ReadLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output False
True Ook op onze user-defined valuetypes kunnen we deze Equals method gebruiken.
Het gedrag die wordt overgeërfd ( van System.ValueType ) zal de volledige toestand ( de verzameling van alle velden ) van de instanties vergelijken.
Slechts indien de X en Y waarde van twee Coordinate instanties gelijk zijn, zullen deze twee instanties als logisch gelijk worden beschouwd : Visual Basic 2010 Broncode Namespace Example3 Structure Coordinate Public Sub New( ByVal x As Integer, ByVal y As Integer) Me.X = x Me.Y = y End Sub Private m_X As Integer Public Property X() As Integer Get X = m_X End Get Set( ByVal value As Integer) m_X = value End Set End Property Private m_Y As Integer Public Property Y() As Integer Get Y = m_Y End Get Set( ByVal value As Integer) m_Y = value End Set End Property End Structure Class Client1 Public Shared Sub Main() Dim coordinate1 As New Coordinate(1, 2) Dim coordinate2 As New Coordinate(1, 3) Dim coordinate3 As New Coordinate(1, 2) Console.WriteLine(coordinate1.Equals(coordinate2)) Console.WriteLine(coordinate1.Equals(coordinate3)) Console.ReadLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output False
True Het was met bovenstaande Coordinate niet mogelijk om twee instanties van deze structure te gaan vergelijken met de = of <> operatoren.
Hier is het gebruik van de Equals method dus noodzakelijk indien we logische gelijkheid wensen na te gaan.
De Equals method die elke valuetype overerft, is Overridable gedefinieerd. Dit maakt het mogelijk om het gedrag, of dus de manier waarop logische gelijkheid wordt bepaald, aan te passen door deze method te gaan overschrijven : Visual Basic 2010 Broncode Namespace Example4 Structure Coordinate Public Sub New( ByVal x As Integer, ByVal y As Integer) Me.X = x Me.Y = y End Sub Private m_X As Integer Public Property X() As Integer Get X = m_X End Get Set( ByVal value As Integer) m_X = value End Set End Property Private m_Y As Integer Public Property Y() As Integer Get Y = m_Y End Get Set( ByVal value As Integer) m_Y = value End Set End Property Public Overrides Function Equals( ByVal obj As Object) As Boolean If obj IsNot Nothing AndAlso TypeOf obj Is Coordinate Then Equals = ( Me.X = DirectCast(obj, Coordinate).X) End If End Function End StructureEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
In bovenstaande versie van Coordinate is vastgelegd dat twee instanties logisch gezien gelijk zijn aan elkaar indien ze dezelfde X waarde hebben.
Onderstaande client zal nu alle Coordinate instanties als logisch gelijk beschouwen omdat ze dezelfde X waarde hebben : Visual Basic 2010 Broncode Namespace Example4 Class Client Public Shared Sub Main() Dim coordinate1 As New Coordinate(1, 2) Dim coordinate2 As New Coordinate(1, 3) Dim coordinate3 As New Coordinate(1, 2) Console.WriteLine(coordinate1.Equals(coordinate2)) Console.WriteLine(coordinate1.Equals(coordinate3)) Console.ReadLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output True
True De implementatie van bovenstaande Equals method, namelijk : Equals = (Me.X = DirectCast(obj, Coordinate).X) kunnen we ook schrijven als : Equals = Me.X.Equals(DirectCast(obj, Coordinate).X).
De X property van het type Integer heeft immers ook de Equals method, bruikbaar om twee Integer instanties met elkaar te vergelijken. boven
13.4.3. Logische Gelijkheid tussen Reference TypesLogische gelijkheid is minder éénduidig dan fysieke gelijkheid, waarop logische gelijkheid wordt gebaseerd is afhankelijk van het type van de vergeleken objecten.
Men zou bijvoorbeeld twee Person objecten logische gelijk kunnen beschouwen als ze dezelfde naam hebben. Of twee Addition objecten kunnen beschouwen als logisch gelijk wanneer ze hetzelfde resultaat hebben.
Van het type System.Object erft iedere referencetype een Equals(obj As Object) As Boolean functie over. Het is trouwens deze die door System.ValueType wordt overgeërfd en geherdefinieerd.
De implementatie die echter door de referencetypes ( rechtstreeks ) van System.Object wordt overgeërfd zal de referenties ( net als de Is operator of de ReferenceEquals method ) gaan vergelijken. By default gaat het dus om fysieke gelijkheid die door deze implemenatie wordt nagegaan : Visual Basic 2010 Broncode Namespace Example5 Class Person Private m_Name As String Public Property Name() As String Get Name = m_Name End Get Set( ByVal value As String) m_Name = value End Set End Property End Class Class Client Public Shared Sub Main() Dim person1 As New Person With {.Name = "John"} Dim person2 As New Person With {.Name = "John"} Dim person3 As Person = person1 Console.WriteLine(person1.Equals(person2)) Console.WriteLine(person1.Equals(person3)) Console.ReadLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output False
True Zoals we reeds gezien hebben is deze Equals method echter wel Overridable, wat maakt dat we het gedrag van deze kunnen herdefiniëren, en zelf kunnen uitkiezen waarop logische gelijkheid tussen twee instanties van ons type kan worden gebaseerd.
Twee Additon objecten zouden we bijvoorbeeld als logisch gelijk kunnen beschouwen indien ze hetzelfde resultaat ( GetResult() ) hebben : Visual Basic 2010 Broncode Namespace Example6 Class Addition Private m_Operand1 As Integer Public Property Operand1() As Integer Get Operand1 = m_Operand1 End Get Set( ByVal value As Integer) m_Operand1 = value End Set End Property Private m_Operand2 As Integer Public Property Operand2() As Integer Get Operand2 = m_Operand2 End Get Set( ByVal value As Integer) m_Operand2 = value End Set End Property Public Function GetResult() As Integer GetResult = Operand1 + Operand2 End Function Public Overrides Function Equals( ByVal obj As Object) As Boolean If obj IsNot Nothing AndAlso TypeOf obj Is Addition Then Equals = _ GetResult().Equals( DirectCast(obj, Addition).GetResult()) End If End Function End Class Class Client1 Public Shared Sub Main() Dim addition1 As New Addition With {.Operand1 = 2, .Operand2 = 3} Dim addition2 As New Addition With {.Operand1 = 2, .Operand2 = 4} Dim addition3 As New Addition With {.Operand1 = 4, .Operand2 = 1} Console.WriteLine(addition1.Equals(addition2)) Console.WriteLine(addition1.Equals(addition3)) Console.ReadLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output False
True boven
13.4.4. Hoe de Equals Method ImplementerenAls x, y en z object expressies zijn, verschillend van Nothing, dan moet het volgende gelden :
- x.Equals(x) is True ( reflexief ) - x.Equals(y) levert hetzelfde op als y.Equals(x) ( symmetrisch ) - als x.Equals(y) en y.Equals(z) True zijn dan is ook x.Equals(z) True ( transitief ) - zolang de toestand van x en y ongewijzigd blijven, zullen herhaaldelijke calls naar x.Equals(y) hetzelfde resultaat opleveren ( consistent ) - x.Equals(Nothing) levert False op
De implementatie van de Equals method mag geen exceptie opgooien.
Datatypes die de IComparable interface implementeren moeten Equals herdefiniëren. Zie het topic over IComparable voor meer details over deze interface.
Indien de Equals method door een type overschreven wordt, moet dit type eigenlijk ook de van Object overgeërfde GetHashCode() method overschrijven. Zie het topic over Dictionaries, HashTable en GetHashCode voor meer details. boven
13.4.5. Equals Symmetrie en InheritanceStel dat twee Counter objecten als logisch gelijk worden beschouwd indien ze dezelfde Value bevatten en twee StepCounter objecten als logisch gelijk worden beschouwd indien ze dezelfde Value en StepValue bevatten.
Dan druist onderstaande implementatie van de Equals method in Counter in tegen het symmetrie principe : Visual Basic 2010 Broncode Namespace NoSymmetry Class Client Public Shared Sub Main() Dim counter1 As Counter = New Counter Dim counter2 As Counter = New Counter Console.WriteLine(counter1.Equals(counter2)) Console.WriteLine(counter2.Equals(counter1)) Dim counter3 As Counter = New StepCounter Console.WriteLine(counter1.Equals(counter3)) Console.WriteLine(counter3.Equals(counter1)) Console.ReadLine() End Sub End Class Class Counter Protected _Value As Integer Public ReadOnly Property Value() As Integer Get Value = _Value End Get End Property Public Overridable Sub Raise() _Value += 1 End Sub Public Overrides Function Equals( ByVal obj As Object) As Boolean If obj IsNot Nothing AndAlso _ TypeOf obj Is Counter Then Equals = Value.Equals( DirectCast(obj, Counter).Value) End If End Function End Class Class StepCounter : Inherits Counter Private _StepValue As Integer Public Property StepValue() As Integer Get StepValue = _StepValue End Get Set( ByVal value As Integer) _StepValue = value End Set End Property Public Overrides Sub Raise() _Value += StepValue End Sub Public Overrides Function Equals( ByVal obj As Object) As Boolean If obj IsNot Nothing AndAlso _ TypeOf obj Is StepCounter Then Equals = _ Value.Equals( DirectCast(obj, StepCounter).Value) AndAlso _ StepValue.Equals( DirectCast(obj, StepCounter).StepValue) End If End Function End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output True
True
True
False Indien counter1.Equals(counter3) True is dan zou ook counter3.Equals(counter1) True moeten zijn, wat niet het geval is, zoals je ziet aan bovenstaande regels (1) en (2).
De oorzaak zit hem op regel (3), de call naar counter3.Equals(counter1) zal False opleveren gezien het argument ( counter1 niet van type StepCounter is. Beide ( counter1 en counter3 ) zijn geen StepCounters maar ze zijn wel beide Counters. Hier zou dus de logica voor logische gelijkheid moeten gelden waarbij enkel gekeken wordt naar de Value en niet naar de StepValue. Of met andere woorden de logische gelijkheids logica van Counter.
We zouden dit kunnen oplossen door in de implementatie van StepCounter na te gaan of het argument van een basistype is, en zoja ook naar de logica van de basisklasse te verwijzen ( zie onderstaand regel (3) ) : Visual Basic 2010 Broncode Namespace Symmetry Class Client Public Shared Sub Main() Dim counter1 As NoSymmetry. Counter = New NoSymmetry. Counter Dim counter2 As NoSymmetry. Counter = New NoSymmetry. Counter Console.WriteLine(counter1.Equals(counter2)) Console.WriteLine(counter2.Equals(counter1)) Dim counter3 As NoSymmetry. Counter = New StepCounter Console.WriteLine(counter1.Equals(counter3)) Console.WriteLine(counter3.Equals(counter1)) Console.ReadLine() End Sub End Class Class StepCounter : Inherits NoSymmetry. Counter Private _StepValue As Integer Public Property StepValue() As Integer Get StepValue = _StepValue End Get Set( ByVal value As Integer) _StepValue = value End Set End Property Public Overrides Sub Raise() _Value += StepValue End Sub Public Overrides Function Equals( ByVal obj As Object) As Boolean If obj IsNot Nothing Then If Me.GetType().IsSubclassOf(obj.GetType()) Then Equals = MyBase.Equals(obj) ElseIf TypeOf obj Is StepCounter Then Equals = _ Value.Equals( DirectCast(obj, StepCounter).Value) AndAlso _ StepValue.Equals( DirectCast(obj, StepCounter).StepValue) End If End If End Function End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output True
True
True
True Bemerk op regel (1) en (2) hoe het symmetrie principe nu wel geldt, counter1.Equals(counter3) levert hetzelfde op als counter3.Equals(counter1). boven
13.4.6. Operator OverloadingIndien men het mogelijk wil maken bepaalde operatoren, als = en <> ook te kunnen gebruiken op instanties van user-defined types, kan men "operator overloading" toepassing : Visual Basic 2010 Broncode Namespace Example3 Partial Structure Coordinate Public Shared Operator =( ByVal coordinate1 As Coordinate, _ ByVal coordinate2 As Coordinate) As Boolean Return coordinate1.Equals(coordinate2) End Operator Public Shared Operator <>( ByVal coordinate1 As Coordinate, _ ByVal coordinate2 As Coordinate) As Boolean Return Not coordinate1.Equals(coordinate2) End Operator End Structure Class Client2 Public Shared Sub Main() Dim coordinate1 As New Coordinate(1, 2) Dim coordinate2 As New Coordinate(1, 3) Dim coordinate3 As New Coordinate(1, 2) Console.WriteLine(coordinate1 = coordinate2) Console.WriteLine(coordinate1 <> coordinate2) Console.WriteLine(coordinate1 = coordinate3) Console.WriteLine(coordinate1 <> coordinate3) Console.ReadLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output False
True
True
False Ook voor referencetypes is dit mogelijk : Visual Basic 2010 Broncode Namespace Example6 Partial Class Addition Public Shared Operator =( ByVal addition1 As Addition, _ ByVal addition2 As Addition) As Boolean Return addition1.Equals(addition2) End Operator Public Shared Operator <>( ByVal addition1 As Addition, _ ByVal addition2 As Addition) As Boolean Return Not addition1.Equals(addition2) End Operator End Class Class Client2 Public Shared Sub Main() Dim addition1 As New Addition With {.Operand1 = 2, .Operand2 = 3} Dim addition2 As New Addition With {.Operand1 = 2, .Operand2 = 4} Dim addition3 As New Addition With {.Operand1 = 4, .Operand2 = 1} Console.WriteLine(addition1 = addition2) Console.WriteLine(addition1 <> addition2) Console.WriteLine(addition1 = addition3) Console.WriteLine(addition1 <> addition3) Console.ReadLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output False
True
True
False Indien een type de operatoren = en <> overladen worden, moet ook Equals door dit type geherdefinieerd worden.
Voor meer details kan je terecht bij het topic over operator overloading.
Dit artikel is gepubliceerd op zondag 31 juli 2011 op vbvoorbeelden, bezoek de website voor een recente versie van dit artikel of andere artikels.
|