| datum |
laatst gewijzigd op dinsdag 12 mei 2009, laatst gepubliceerd op woensdag 28 april 2010 |
| broncode |
Download Properties.vb of Properties.cs |
| hoofdstuk |
8. 9. 10.  |
| onderwerp |
9.1. 9.2. Properties 9.3.  |
| rubrieken | 





|
Dit artikel is gepubliceerd op woensdag 28 april 2010 op vbvoorbeelden, bezoek de website voor een recente versie van dit artikel of andere artikels.
9.2.1. Getter ( Get ) en Setter ( Set )Specifiek voor een aantal programmeertalen zijn de Property constructies. Deze vervangen de get-function en/of de set procedure ( en worden ook zo op de achtergrond vertaald ) : Visual Basic Broncode Namespace Example1
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
Module Client
Sub Main()
Dim person1 As Person = New Person
person1.Name = "John"
Console.WriteLine(person1.Name)
Console.ReadLine()
End Sub
End Module
End NamespaceDownload Visual Basic Broncode Bekijk deze Broncode in Visual C#
De getter (1) van de property wordt uitgevoerd wanneer de property wordt uitgelezen (4). De setter (2) wordt uitgevoerd bij het instellen van de property waarde (3). De toegekende waarde ( hier de String literal "John" ) wordt ge-assigneerd aan de argument variable value van de setter.
Zoals je merkt is de implementatie van de getter en setter identiek aan respectievelijk de get functie en set procedure uit vorig voorbeeld.
Anders is wel het gebruik voor de client. Deze client kan de property nu aanspreken alsof het een publiek veld zou betreffen van dat object. De toekenning aan deze property (3) gebeurt op dezelfde wijze als een toekenning aan een String variabele. Het uitlezen van de property (4) vormt hier een String expressie, net zoals de aanroep naar de get functie uit vorig voorbeeld. Dit maakt ook duidelijk dat het hier een String expressie zal zijn die als returnwaarde wordt opgegeven in de getter van de property.
De argumentvariabele van de setter krijgt by default de identifier value, dit kan aangepast worden, maar hier is echter geen reden toe. Wat niet aangepast kan worden, of wat steeds gelijk moet zijn, is het type vermeld in de As clausule van de signatuurregel van de property zelf en het type vermeld in de As clausule van de signatuurregel van de setter ( in bovenstaand voorbeeld dus beide String ).
Ook via een publiek veld kunnen we in de client op dezelfde wijzen een eigenschap inlezen of uitlezen : Visual Basic Broncode Namespace Example2
Class Book
Public Title As String
End Class
Module Client
Sub Main()
Dim book1 As Book = New Book
book1.Title = "Some Title"
Console.Write(book1.Title)
Console.ReadLine()
End Sub
End Module
End NamespaceDownload Visual Basic Broncode Bekijk deze Broncode in Visual C#
Visual Basic Output Some Title Het voordeel van het werken met een Property-constructie is echter dat er nu binnen de implementatie van de getter en de setter ruimte is voor validatie. De getter kan bijvoorbeeld de inhoud van het veld ( gebruikt voor de opslag van de eigenschap ) valideren ( en/of bewerken ) alvorens de terugkeerwaarde in te stellen. De setter kan dan bijvoorbeeld de toegekende waarde ( inhoud van value ) valideren ( en/of bewerken ) alvorens het gerelateerde veld op te vullen.
Validatie is natuurlijk geen specifiek kenmerk voor de propertyconstructie, ook in getfunctions en setprocedures kan gelijkaardige validatie gebeuren.
Velden worden zelden publiek gemaakt. Deze zijn immers doorgaans louter noodzakelijk om een toestand te bewaren. Het bewaren van een toestand is een implementatiekwesties. En implementatie-kwesties worden steeds ingekapseld.
Tot en met versie 2005 waren er weinig significante voordelen van het gebruik van properties in tegenstelling tot het gebruik van get-functions en set procedure. De keuze voor een van beide was dan ook vooral afhankelijk van de voorkeur van de auteur van de klasse. Sedert Visual Basic 2008 biedt het gebruik van properties in tegenstelling tot het gebruik van set procedures en get functies meerdere voordelen. Verschillende design coding guidelines geven aan dat men properties gebruikt indien je rechtstreekse werkt op een ingekapseld veld die de respresentatie van deze eigenschap verwezelijkt en anderzijds getfuncties en setprocedures gaat gebruiken indien de implementatie complexer is ( en een langere uitvoeringstijd nodig heeft ) dan het rechtstreekse gebruik van een ingekapseld veld.
Bemerk dat de identifier van Propertys doorgaans zelfstandig-naamwoorden ( bijvoorbeeld Name ) zullen zijn, in tegen stelling tot methods die doorgaans een werkwoord of een combinatie van een werkwoord en een zelfstandig-naamwoord ( bijvoorbeeld GetName, SetName, ... ) zullen zijn. Terug naar boven 9.2.2. Access Modifiers voor Getters en SettersDe access modifier van enkel de getter of enkel setter van een property kan beperkt worden ( bijvoorbeeld Private in plaats van Public ) ten aanzien van de property zelf.
Hieronder beschikt een Counter klasse over een publieke Value property met een ingekapselde setter (1) : Visual Basic Broncode Namespace Example3
Class Counter
Private m_Value As Integer
Public Property Value() As Integer
Get
Value = m_Value
End Get
Private Set(ByVal value As Integer)
m_Value = value
End Set
End Property
Public Sub Raise()
Value = Value + 1
End Sub
End Class
End NamespaceDownload Visual Basic Broncode Bekijk deze Broncode in Visual C#
Binnen de klasse zelf kan Value zowel uitgelezen als ingelezen worden (2), alle members van een klasse ( of ze nu ingekapseld zijn of niet ) zijn immers beschikbaar binnen de klasse zelf.
Zoals hieronder gedemonstreerd, is het voor clients niet mogelijk om de setter van de property Value te gebruiken (3). Visual Basic Broncode Namespace Example3
Module Client
Sub Main()
Dim counter1 As Counter = New Counter
Console.WriteLine(counter1.Value)
counter1.Raise()
Console.WriteLine(counter1.Value)
counter1.Raise()
Console.WriteLine(counter1.Value)
Console.ReadLine()
End Sub
End Module
End NamespaceDownload Visual Basic Broncode Bekijk deze Broncode in Visual C#
Men bekomt hier voor client als het ware een "readonly" eigenschap. Binnen de klasse zelf is de property echter wel "writeable".
Abstractiegewijs komt het zelden voor dat getters worden ingekapseld wanneer de setter publiek toegankelijk is. Terug naar boven
9.2.3. ReadOnly PropertyRegelmatig zal het voorkomen dat property enkel uitleesbaar moet zijn, en dus niet instelbaar. Typische voorbeelden hiervoor zijn berekende en afleidbare eigenschappen. Als men bijvoorbeeld voor een Product de Price en het TaxPercentage kent, kan men hieruit ook de PriceIncludingTax bepalen.
Dergelijke abstracties kunnen we bekomen door gebruik te maken van een ReadOnly Property, deze heeft geen setter, maar bepaald enkel in zijn getter welke waarde wordt opgeleverd.
Hieronder een voorbeeld : Visual Basic Broncode Namespace Example4
Class Product
Private m_Price As Decimal
Public Property Price() As Decimal
Get
Price = m_Price
End Get
Set(ByVal value As Decimal)
m_Price = value
End Set
End Property
Private m_TaxPercentage As Decimal
Public Property TaxPercentage() As Decimal
Get
TaxPercentage = m_TaxPercentage
End Get
Set(ByVal value As Decimal)
m_TaxPercentage = value
End Set
End Property
Public ReadOnly Property PriceIncludingTax() As Decimal
Get
PriceIncludingTax = Price * (1 + (TaxPercentage / 100))
End Get
End Property
End Class
Module Client
Sub Main()
Dim product1 As Product = New Product
product1.Price = 100
product1.TaxPercentage = 8
Console.WriteLine(product1.PriceIncludingTax)
Console.ReadLine()
End Sub
End Module
End NamespaceDownload Visual Basic Broncode Bekijk deze Broncode in Visual C#
In bovenstaande Client kan dus geen waarde worden toegekend aan de property PriceIncludingTax.
De mogelijkheid bestaat uiteraard om ook die eigenschap writeable te maken, de keuze hangt vooral af van wat je in de client als input wil gebruiken : Price en Tax als input en PriceIncludingTax als afgeleide waarde of Price en PriceIncludingTax als input en Tax als afgeleide waarde of Tax en PriceIncludingTax als input en Price als afgeleide waarde Alle bovenstaande variaties zijn mogelijk, maar de eerst-vermeld lijkt misschien wel de meest logische. Men zou er ook voor kunnen opteren alle 3 de eigenschappen writeable te maken, maar dan bekom je een vreemde situatie. Als bijvoorbeeld de PriceIncludingTax wordt ingesteld moet je dan op basis hiervan en op basis van de Tax de Price afleiden, of moet je op basis hiervan en op basis van de Price de Tax afleiden. Je ziet het wordt verwarrend. Eender welke keuze je uiteindelijk ook zou maken, steeds zal je ervoor moeten zorgen dat de client-auteur hiervan op de hoogte kan zijn ( door dit aspect bijvoorbeeld in de klassedocumentatie te verduidelijken ). Maar hoe dan ook dergelijke verwarrende situatie is beter te vermijden. Het opstellen van een goede abstractie is continue zoeken naar een delicaat evenwicht, net voldoende mogelijkheden bieden aan de client van die klasse zonder verwarring te creëren is de kunst.
Merk ook op dat in bovenstaand voorbeeld in de implementatie van de property PriceIncludingTax er gebruik wordt gemaakt van de ( publiek toegankelijke ) properties Price en TaxPercentage, en dus niet van de - in de klasse zelf ook toegankelijke - velden m_Price en m_TaxPercentage. Dit is doorgaans de beste keuze omdat je zeker kunt stellen dat de publieke members ( die je dus ook aan de clients aanbiedt ) steeds een voorstelling zal blijven van Price en TaxPercentage ( eventueel inclusief hun validaties of bewerkingen ). Dit zal steeds zo blijven omdat de betekenis van die members gegarandeerd zou moeten zijn, dit kan je niet zeggen van de gerelateerde velden die gebruikt worden voor de interne opslag, deze kunnen eender welke voorstelling zijn, die nog gevalideerd of gemanipuleerd kunnen worden alvorens tot hetgeen te komen dat echt de prijs of de tax voorstelt. Zelfs al gaat het niet om een publieke property dan zal het om dezelfde redenen doorgaans nog altijd interessanter zijn deze te gebruiken in plaats van een eventueel gerelateerd veld.
In dit voorbeeld merk je ook dat een property-constructie niet noodzakelijk moet gekoppeld worden aan een veld. Binnen de implementatie van de getter en de setter kan men eender welke code uitvoeren.
Hoewel deze PriceIncludingTax te verwezelijken was via een ReadOnly Property kunnen we hiervoor ook een getfunctie gebruiken, dit is ook wat de coding guidelines ons aanraden in het geval dat de implementatie meer inhoud dan enkel het ophalen van een veldwaarde : Visual Basic Broncode Namespace Example5
Class Product
Private m_Price As Decimal
Public Property Price() As Decimal
Get
Price = m_Price
End Get
Set(ByVal value As Decimal)
m_Price = value
End Set
End Property
Private m_TaxPercentage As Decimal
Public Property TaxPercentage() As Decimal
Get
TaxPercentage = m_TaxPercentage
End Get
Set(ByVal value As Decimal)
m_TaxPercentage = value
End Set
End Property
Public Function GetPriceIncludingTax() As Decimal
GetPriceIncludingTax = Price * (1 + (TaxPercentage / 100))
End Function
End Class
Module Client
Sub Main()
Dim product1 As Product = New Product
product1.Price = 100
product1.TaxPercentage = 8
Console.WriteLine(product1.GetPriceIncludingTax())
Console.ReadLine()
End Sub
End Module
End NamespaceDownload Visual Basic Broncode Bekijk deze Broncode in Visual C#
Visual Basic Output 108,00 Terug naar boven
9.2.4. ReadOnly FieldsOok velden kunnen ReadOnly gedeclareerd worden.
Aan deze kan enkel een waarde worden toegewezen in de constructor.
Dit wordt gebruikt voor bevroren toestanden, niet te verwarren met constanten. Een bevroren toestand ( in tegenstelling tot een constante ) kan ingesteld worden at runtime ( enkel in de constructor ), maar zal daarna nooit meer van waarde veranderen ( net als een constante ).
Voor meer details over constructoren en ReadOnly velden, kan je het artikel over constructoren raadplegen. Terug naar boven
9.2.5. WriteOnly PropertyNaast ReadOnly Property kan men ook een WriteOnly Property opstellen, deze heeft dan geen getter. Maar abstractiegewijs zijn er bijna geen situaties waarin dit bruikbaar is. Je zou je kunnen afvragen voor welke eigenschap het nuttig kan zijn het toe te laten een bepaalde waarde in te stellen, maar niet toe te laten deze waarde ( die toch reeds gekend is omdat de client deze heeft ingesteld ) op te vragen. Een WriteOnly Property is dan ook zeldzaam en getuigd meestal van een povere abstractie, ik verkies dan ook hiervan geen voorbeeld op te nemen. Terug naar boven
9.2.6. OefeningOpgave : Maak een klasse waarvan objecten een abstractie vormen van een eenvoudige bankrekening.
Een bankrekening heeft een bepaald saldo ( dat onbeperkt positief of negatief ) kan zijn. Initiëel ( voor een nieuwe rekening ) is het saldo nul.
Van een bankrekening kan een bepaald bedrag worden afgehaald, en op een bankreking kan een bepaald bedrag worden gestort.
Een bedrag van een bankrekening kan ook worden overschreven naar een ( andere ) bankrekening. Overschrijven wordt beschouwd als het afhalen van een rekening en storten op een ( andere ) rekening. Oplossing : Visual Basic Broncode Module BankAccountTestFixture
Sub Main()
Dim bankAccount1 As BankAccount = New BankAccount
Console.WriteLine(bankAccount1.Balance = 0)
bankAccount1.Deposit(100)
Console.WriteLine(bankAccount1.Balance = 100)
bankAccount1.Withdraw(60)
Console.WriteLine(bankAccount1.Balance = 40)
Dim bankAccount2 As BankAccount = New BankAccount
bankAccount1.Transfer(50, bankAccount2)
Console.WriteLine(bankAccount1.Balance = -10)
Console.WriteLine(bankAccount2.Balance = 50)
Console.ReadLine()
End Sub
End Module
Class BankAccount
Private m_Balance As Decimal
Public ReadOnly Property Balance() As Decimal
Get
Balance = m_Balance
End Get
End Property
Public Sub Deposit(ByVal amount As Decimal)
m_Balance += amount
End Sub
Public Sub Withdraw(ByVal amount As Decimal)
m_Balance -= amount
End Sub
Public Sub Transfer(ByVal amount As Decimal, ByVal target As BankAccount)
Withdraw(amount)
target.Deposit(amount)
End Sub
End ClassDownload Visual Basic Broncode Bekijk deze Broncode in Visual C#
Visual Basic Output True
True
True
True
True
Dit artikel is gepubliceerd op woensdag 28 april 2010 op vbvoorbeelden, bezoek de website voor een recente versie van dit artikel of andere artikels.
Visual Basic 2008 & 2010 Boeken
Berichten
|