|
Dit artikel is gepubliceerd op zondag 31 juli 2011 op vbvoorbeelden, bezoek de website voor een recente versie van dit artikel of andere artikels.
21.3.1. System.IComparableIn de FCL ( Framework Class Library ) zitten reeds tal van voorgedefinieerde interfacetypes. Deze zijn dus meteen klaar voor gebruik en worden zelfs al in andere types van de FCL gebruikt.
Een voorbeeld daarvan is de IComparable interface uit de System namespace. Deze wordt onder andere gebruikt in de klassen System.Array en System.Collections.ArrayList waar men beschikt over Sort methods.
Deze Sort methods gaan een bepaald sorteringsprincipe toepassen op de elementen van de collectie. Bij numerieke inhoud van een Array of ArrayList zal Sort bijvoorbeeld de waarden van klein naar groot sorteren, bij Dates zal dit van oud naar nieuw zijn, ... . Visual Basic 2010 Broncode Namespace Example1 Class Client Public Shared Sub Main() Dim byteArray1 As Byte() = New Byte() {3, 2, 1} Array.Sort(byteArray1) For Each item As Byte In byteArray1 Console.Write(item.ToString() & " ") Next Console.WriteLine() Dim arrayList1 As ArrayList = New ArrayList() arrayList1.Add( New Date(2006, 10, 22)) arrayList1.Add( New Date(2006, 10, 21)) arrayList1.Add( New Date(2006, 10, 20)) arrayList1.Sort() For Each item As Date In arrayList1 Console.Write(item.ToString() & " ") Next Console.WriteLine() Console.ReadLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output 1 2 3
20/10/2006 0:00:00 21/10/2006 0:00:00 22/10/2006 0:00:00 De manier waarop de elementen van een collectie gesorteerd worden is dus afhankelijk van het elementtype. Elk type die je definieert kan de IComparable interface implementeren, om zo zelf te bepalen hoe elementen van dat type met elkaar vergeleken moeten worden.
Als we elementen van onze eigen user defined types wensen te sorteren in onder andere een Array of een ArrayList. Dan kunnen we dit doen door de IComparable interface in ons type te implementeren.
Hiervoor hoeven we enkel een implementatie te voorzien voor : Function CompareTo(ByVal obj As Object) As Integer
Dit is een functie die een negatieve waarde, nul of positieve waarde oplevert naargelang het doorgegeven argument respectievelijk "groter", "gelijk" of "kleiner" is dan het object zelf ( waarop de CompareTo method wordt aangeroepen ).
Door deze implementatie te voorzien kunnen methods als Sort ( uit bijvoorbeeld de types Array, ArrayList, ... ) twee elementen met elkaar vergelijken gebaseerd op de terugkeerwaarde van deze CompareTo. Om elementen in een verzameling te sorteren, moet je immers in staat zijn twee elementen onderling met elkaar te vergelijken om te weten of het ene element voor of achter het andere element geplaatst moet worden.
In onderstaand voorbeeld gaan we de volgorde in een verzameling waarop elementen van het type Figure moeten geplaatst worden baseren op de GetArea() van deze figuren : Visual Basic 2010 Broncode Namespace Example2 MustInherit Class Figure : Implements IComparable Public MustOverride Function GetArea() As Double Private Function CompareTo( ByVal obj As Object) As Integer _ Implements System.IComparable. CompareTo CompareTo = CompareTo( DirectCast(obj, Figure)) End Function Public Function CompareTo( ByVal other As Figure) As Integer CompareTo = Me.GetArea().CompareTo(other.GetArea()) Select Case Me.GetArea() Case Is > other.GetArea() CompareTo = 1 Case Is = other.GetArea() CompareTo = 0 Case Is < other.GetArea() CompareTo = -1 End Select End Function Public Overrides Function ToString() As String ToString = GetArea.ToString() End Function End Class Class Square : Inherits Figure Private m_Side As Double Public Property Side() As Double Get Side = m_Side End Get Set( ByVal Value As Double) m_Side = Value End Set End Property Public Overrides Function GetArea() As Double GetArea = Side ^ 2 End Function End Class Class Circle : Inherits Figure Private m_Radius As Double Public Property Radius() As Double Get Radius = m_Radius End Get Set( ByVal Value As Double) m_Radius = Value End Set End Property Public Overrides Function GetArea() As Double GetArea = (Radius ^ 2) * System.Math.PI End Function End Class Class Client Public Shared Sub Main() Dim square1 As Square = New Square With {.Side = 5} Dim square2 As Square = New Square With {.Side = 3} Dim circle1 As Circle = New Circle With {.Radius = 1} Dim figures1 As Figure() = New Figure() {square1, circle1, square2} For Each item As Figure In figures1 Console.Write(item.ToString() & " ") Next Console.WriteLine() Array.Sort(figures1) For Each item As Figure In figures1 Console.Write(item.ToString() & " ") Next Console.WriteLine() Dim figures2 As ArrayList = New ArrayList figures2.Add(square1) figures2.Add(circle1) figures2.Add(square2) For Each item As Figure In figures2 Console.Write(item.ToString() & " ") Next Console.WriteLine() figures2.Sort() For Each item As Figure In figures2 Console.Write(item.ToString() & " ") Next Console.WriteLine() Console.WriteLine(square1.CompareTo(circle1)) Console.WriteLine(circle1.CompareTo(square1)) Dim somethingComparable As IComparable = square2 Console.ReadLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output 25 3,14159265358979 9
3,14159265358979 9 25
25 3,14159265358979 9
3,14159265358979 9 25
1
-1 Ook abstract klassen kunnen een interface implementeren. Abstract klassen kunnen voor de members uit het interfacetype een implementatie voorzien. Toch is dit niet noodzakelijk, ook in een MustOverride member kan een Implements clausule worden opgenomen. boven
21.3.2. Private ImplementationIn bovenstaand voorbeeld wordt "private implementation" toegepast. Men spreekt over private implementation in het geval dat members uit een interfacetype niet worden geïmplementeerd door een Public member. Dit zou men bijvoorbeeld kunnen gebruiken ( zoals ook in bovenstaand voorbeeld ) om te vermijden dat niet typesafe members aan de interface ( publieke members ) van het implementerende type worden toegevoegd. Een typesafe CompareTo method werd wel toegevoegd, om de mogelijkheid te voorzien meerdere Figure objecten met elkaar te vergelijken. Aan deze method kunnen nu enkel Figure expressies worden doorgegeven. Regel (1) en (2) zijn hiervan voorbeelden. Regel (3) demonstreert dat het niet mogelijk is niet Figure expressies door te geven. Wat echter wel nog mogelijk is, is om via expressies van het type IComparable de niet typesafe CompareTo method aan te roepen ( zoals regel (4) illustreert ). Dit zullen we echter nooit kunnen verhinderen. Een runtimefout treed op bij de uitvoer van deze regel.
Een ander manier om enkel een typesafe CompareTo method te voorzien, bestaat erin in plaats van de System.IComparable interface de System.IComparable(Of T) interface te implementeren. In het hoofdstuk over genericiteit wordt deze interface behandeld.
Overigens is het zo dat een interfacetype enkele kan worden geïmplementeerd in een klasse, een interface kan zelf geen andere interface implementeren, maar kan wel van andere interfaces overerven.
De implementatie voor de CompareTo method uit het interfacetype IComparable in de klasse Figure kan nog vereenvoudigd worden.
We baseren de CompareTo returnwaarde op de GetArea, door de GetArea van het argument met de GetArea van het object zelf te vergelijken.
Deze GetArea functie is van het type Double. Nu weten me ook dat bijvoorbeeld een Array met elementen van het type Double kan worden gesorteerd via de Array.Sort method. Ook dit komt doordat het type Double in een implementatie van de IComparable interface voorziet. We kunnen de implementatie voor onze CompareTo implementatie vervolgens gewoon delegeren naar de implementatie voor CompareTo die het Double type hiervoor heeft voorzien. Visual Basic 2010 Broncode Namespace Example4 MustInherit Class Figure : Implements IComparable Public MustOverride Function GetArea() As Double Private Function CompareTo( ByVal obj As Object) As Integer _ Implements System.IComparable. CompareTo CompareTo = CompareTo( DirectCast(obj, Figure)) End Function Public Function CompareTo( ByVal other As Figure) As Integer CompareTo = Me.GetArea().CompareTo(other.GetArea()) End Function Public Overrides Function ToString() As String ToString = GetArea.ToString() End Function End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
boven
21.3.3. Het IComparable.CompareTo ContractEr zijn enkele condities waaraan de implementatie van de CompareTo method aan moet voldoen :
- Een ArgumentException treedt op indien je een object van een bepaald type zou proberen te vergelijken via CompareTo met een object van een ongerelateerd type (1).
- Elke object is "groter" dan Nothing (2). <any-object>.CompareTo(Nothing) levert dus een positieve waarde op.
CompareTo van Figure moeten we dus als volgt verbeteren : Visual Basic 2010 Broncode Namespace Example5 MustInherit Class Figure : Implements IComparable Public MustOverride Function GetArea() As Double Private Function CompareTo( ByVal obj As Object) As Integer _ Implements System.IComparable. CompareTo If Not TypeOf obj Is Figure Then Throw New ArgumentException( "An invalid argument was " & _ "specified. An argument of type Figure is required.") Else CompareTo = CompareTo( DirectCast(obj, Figure)) End If End Function Public Function CompareTo( ByVal other As Figure) As Integer If other IsNot Nothing Then CompareTo = 1 Else CompareTo = Me.GetArea().CompareTo(other.GetArea()) End If End Function Public Overrides Function ToString() As String ToString = GetArea.ToString() End Function End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Andere afspraken in het contract van IComparable.CompareTo zijn :
- x.CompareTo(x) levert nul op.
- Als x.CompareTo(y) nul oplevert dan zou ook y.CompareTo(x) nul moeten opleveren.
- Als zowel x.CompareTo(y) als y.CompareTo(z) nul opleveren dan moet ook x.CompareTo(z) nul opleveren.
- Als x.CompareTo(y) een positieve waarde oplevert dan moet y.CompareTo(x) een negatieve waarde opleveren, en vice versa.
- Als x.CompareTo(y) en y.CompareTo(z) beide waardes opleveren met een bepaald teken ( positief of negatief ) dan moet ook x.CompareTo(z) een waarde opleveren met hetzelfde teken. boven
21.3.4. OefeningenOpgave :
Zorg ervoor dat in onderstaande code, een collectie van Counter objecten kan gesorteerd worden. Of dus dat verschillende Counter objecten onderling met elkaar vergeleken kunnen worden. Visual Basic 2010 Broncode Namespace Exercise1 Class Counter Private m_Value As Integer Public Property Value() As Integer Get Value = m_Value End Get Set( ByVal value As Integer) m_Value = value End Set End Property Public Overrides Function ToString() As String ToString = Value.ToString() End Function End Class Class Client Public Shared Sub Main() Dim counter1 As Counter = New Counter With {.Value = 1} Dim counter2 As Counter = New Counter With {.Value = 2} Dim countersArrayList1 As ArrayList = New ArrayList countersArrayList1.Add(counter2) countersArrayList1.Add(counter1) Print(countersArrayList1) countersArrayList1.Sort() Print(countersArrayList1) Console.ReadLine() End Sub Public Shared Sub Print( ByVal arrayList As ArrayList) For Each element As Object In arrayList Console.Write(element.ToString() & " ") Next Console.WriteLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output 2 1
1 2 Oplossing : Visual Basic 2010 Broncode Namespace Exercise1 Partial Class Counter : Implements IComparable Public Function CompareTo( ByVal obj As Object) As Integer _ Implements System.IComparable. CompareTo If obj Is Nothing Then CompareTo = 1 ElseIf Not TypeOf obj Is Counter Then Throw New ArgumentException( "An invalid argument was " & _ "specified. An argument of type Counter is required.") Else CompareTo = Value.CompareTo( DirectCast(obj, Counter).Value) End If End Function End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Opgave :
Bestudeer onderstaande klasse Counter : Visual Basic 2010 Broncode Namespace Exercise2 Class Counter : Implements IComparable Private m_Value As Integer Public Property Value() As Integer Get Value = m_Value End Get Set( ByVal value As Integer) m_Value = value End Set End Property Public Overrides Function ToString() As String ToString = Value.ToString() End Function Public Function CompareTo( ByVal obj As Object) As Integer _ Implements System.IComparable. CompareTo If obj Is Nothing Then CompareTo = 1 ElseIf Not TypeOf obj Is Counter Then Throw New ArgumentException( "An invalid argument was " & _ "specified. An argument of type Counter is required.") Else CompareTo = Value.CompareTo( DirectCast(obj, Counter).Value) End If End Function End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Zoals onderstaande Test1CompareTo aangeeft kunnen verschillende objecten van deze klasse Counter kunnen onderling met elkaar vergeleken worden via de CompareTo implementatie uit de IComparable interface. Visual Basic 2010 Broncode Namespace Exercise2 Class Client Private Shared counter1 As Counter = New Counter With {.Value = 1} Private Shared counter2 As Counter = New Counter With {.Value = 2} Public Shared Sub Test1CompareTo() Console.WriteLine( "Test1CompareTo :") Console.WriteLine(counter1.CompareTo(counter1)) Console.WriteLine(counter1.CompareTo(counter2)) Console.WriteLine(counter2.CompareTo(counter1)) Console.ReadLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output Test1CompareTo :
0
-1
1 Wat echter ook mogelijk is ( door de compiler wordt toegelaten ), en wat onderstaande Test2CompareTo ook aangeeft, is dat eender welk ander object vergeleken wordt met een Counter object. De CompareTo implementatie uit Counter werkt hier immers met een Object argument, wat maakt dat argumentwaarden van eender welk datatype aan deze CompareTo method kunnen worden doorgegeven. Visual Basic 2010 Broncode Namespace Exercise2 Partial Class Client Public Shared Sub Test2CompareTo() Console.WriteLine( "Test2CompareTo :") Console.WriteLine(counter1.CompareTo( "dummy")) End Sub Public Shared Sub Main() Test1CompareTo() Test2CompareTo() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Bovenstaande Test2CompareTo zal at runtime een fout geven. De aan de CompareTo doorgegeven String is immers niet van type Counter.
Pas bovenstaande klasse Counter zo aan dat reeds at compiletime het foutieve gebruik zoals in Test2CompareTo verhinderd wordt. Met foutief gebruik wordt hier dus bedoeld : het doorgeven van een niet Counter object aan de de CompareTo implementatie. Zorg er echter wel voor dat verschillende Counter objecten, zoals in Test1CompareTo wel nog met elkaar vergeleken kunnen worden.
Oplossing : Visual Basic 2010 Broncode Namespace Exercise2Solution Class Counter : Implements IComparable Private m_Value As Integer Public Property Value() As Integer Get Value = m_Value End Get Set( ByVal value As Integer) m_Value = value End Set End Property Public Overrides Function ToString() As String ToString = Value.ToString() End Function Private Function CompareTo( ByVal obj As Object) As Integer _ Implements System.IComparable. CompareTo If Not TypeOf obj Is Counter Then Throw New ArgumentException( "An invalid argument was " & _ "specified. An argument of type Counter is required.") Else CompareTo = CompareTo( DirectCast(obj, Counter)) End If End Function Public Function CompareTo( ByVal other As Counter) As Integer If other IsNot Nothing Then CompareTo = 1 Else CompareTo = Me.Value.CompareTo(other.Value) End If End Function End Class Class Client Private Shared counter1 As Counter = New Counter With {.Value = 1} Private Shared counter2 As Counter = New Counter With {.Value = 2} Public Shared Sub Test1CompareTo() Console.WriteLine( "Test1CompareTo :") Console.WriteLine(counter1.CompareTo(counter1)) Console.WriteLine(counter1.CompareTo(counter2)) Console.WriteLine(counter2.CompareTo(counter1)) Console.ReadLine() End Sub Public Shared Sub Test2CompareTo() Console.WriteLine( "Test2CompareTo :") Console.ReadLine() End Sub Public Shared Sub Main() Test1CompareTo() Test2CompareTo() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output Test1CompareTo :
0
-1
1 Opgave :
Creëer een Reservation klasse met een StartDate en EndDate eigenschap.
Een array van objecten van het type Reservation zou sorteerbaar moeten zijn met de Array.Sort method.
Reservaties moeten gesorteerd worden eerst oplopend ( van oud naar nieuw ) volgens startdatum, is deze gelijk dan oplopend ( van oud naar nieuw ) volgens einddatum.
Oplossing : Visual Basic 2010 Broncode Namespace Exercise3 Public Class Reservation : Implements IComparable Public Sub New( ByVal startDate As Date, ByVal endDate As Date) Me.StartDate = startDate Me.EndDate = endDate End Sub Private m_StartDate As Date Public Property StartDate() As Date Get StartDate = m_StartDate End Get Set( ByVal value As Date) m_StartDate = value End Set End Property Private m_EndDate As Date Public Property EndDate() As Date Get EndDate = m_EndDate End Get Set( ByVal value As Date) m_EndDate = value End Set End Property Public Overrides Function ToString() As String ToString = StartDate.ToShortDateString() & "-" & _ EndDate.ToShortDateString() End Function Private Function CompareTo( ByVal obj As Object) As Integer _ Implements System.IComparable. CompareTo If Not TypeOf obj Is Reservation Then Throw New ArgumentException( "An invalid argument was " & _ "specified. An argument of type Reservation is required.") Else CompareTo = CompareTo( DirectCast(obj, Reservation)) End If End Function Public Function CompareTo( ByVal other As Reservation) As Integer If other IsNot Nothing Then CompareTo = 1 Else CompareTo = Me.StartDate.CompareTo(other.StartDate) If CompareTo = 0 Then CompareTo = Me.EndDate.CompareTo(other.EndDate) End If End If End Function End Class Public Class Client Public Shared Sub Main() Dim reservations1 As Reservation() = New Reservation() _ { New Reservation(#4/1/2008#, #4/8/2008#), _ New Reservation(#4/1/2008#, #4/5/2008#), _ New Reservation(#3/30/2008#, #3/31/2008#)} Console.WriteLine( "Unsorted :") For Each element As Reservation In reservations1 Console.WriteLine(element) Next Console.WriteLine() Array.Sort(reservations1) Console.WriteLine( "Sorted :") For Each element As Reservation In reservations1 Console.WriteLine(element) Next Console.ReadLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output Unsorted :
1/04/2008-8/04/2008
1/04/2008-5/04/2008
30/03/2008-31/03/2008
Sorted :
30/03/2008-31/03/2008
1/04/2008-5/04/2008
1/04/2008-8/04/2008 Opgave :
Maak een systeem waarbij verschillende soorten "calculations" ( "berekeningen" ) kunnen worden gemaakt, voorzie minimaal de mogelijkheid om een "division" ( "deling" ) en een "multiplication" ( "vermenigvuldiging" ) te creëren.
Alle berekeningen hebben gemeenschappelijk dat ze een bepaald resultaat ( "quotient", "product", ... ) kunnen opleveren en dat ze herhaald kunnen worden ( "divide by", "multiply by", ... ).
Onderstaande clientcode illustreert wat onder andere mogelijk moet zijn. Visual Basic 2010 Broncode Namespace Exercise4 Class Client Public Shared Sub Main() Dim division1 As Division = New Division(20, 5) Dim division2 As Division = New Division(10, 5) Console.WriteLine(division1.Quotient()) Console.WriteLine(division1.DivideBy(division2)) Dim multiplication1 As Multiplication = New Multiplication(2, 3) Console.WriteLine(multiplication1. Product()) Console.WriteLine(multiplication1.MultiplyBy(division2)) Console.ReadLine() End Sub End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Console Application Output 4
2
6
12 Oplossing : Visual Basic 2010 Broncode Namespace Exercise4 Interface ICalculation Function GetResult() As Decimal Function Repeat( ByVal other As ICalculation) As Double End Interface Class Multiplication : Implements ICalculation Public Sub New( ByVal operand1 As Double, ByVal operand2 As Double) Me.Operand1 = operand1 Me.operand2 = operand2 End Sub Private m_Operand1 As Double Public Property Operand1() As Double Get Operand1 = m_Operand1 End Get Set( ByVal value As Double) m_Operand1 = value End Set End Property Private m_Operand2 As Double Public Property Operand2() As Double Get Operand2 = m_Operand2 End Get Set( ByVal value As Double) m_Operand2 = value End Set End Property Function Product() As Decimal Implements ICalculation. GetResult Product = Convert.ToDecimal(Operand1 * Operand2) End Function Public Function MultiplyBy( ByVal other As ICalculation) As Double _ Implements ICalculation. Repeat MultiplyBy = Product() * other.GetResult() End Function End Class Class Division : Implements ICalculation Public Sub New( ByVal dividend As Double, ByVal divisor As Double) Me.Dividend = dividend Me.Divisor = divisor End Sub Private m_Dividend As Double Public Property Dividend() As Double Get Dividend = m_Dividend End Get Set( ByVal value As Double) m_Dividend = value End Set End Property Private m_Divisor As Double Public Property Divisor() As Double Get Divisor = m_Divisor End Get Set( ByVal value As Double) m_Divisor = value End Set End Property Function Quotient() As Decimal Implements ICalculation. GetResult Quotient = Convert.ToDecimal(Dividend / Divisor) End Function Public Function DivideBy( ByVal other As ICalculation) As Double _ Implements ICalculation. Repeat DivideBy = Quotient() / other.GetResult() End Function End ClassEnd NamespaceDownload Visual Basic 2010 Broncode Download Visual C# Sourcecode
Dit artikel is gepubliceerd op zondag 31 juli 2011 op vbvoorbeelden, bezoek de website voor een recente versie van dit artikel of andere artikels.
|