Scope i .NET
Förord
Scope tillhör något som skulle kunna kallas grunderna i vilket språk som helst och därför måste man kunna det. Det ska finnas i ryggraden på samma sätt som datatyper. I ”klassisk ASP” fanns över huvud taget inget som kunde jämföras med scope och därför riktar sig denna artikel främst till dig som programmerat en del ASP eller VB och sedan hoppat över i .NET. Senare tillägg: Administrativa problem gjorde att artikeln i sitt orginalutförande inte fick med bilder eller kod. Nu finns båda och kanske blir det lättare att förstå.Innehåll
»»
»
»
»
»
»
»
»
Relaterade artiklar
» Datatyper i .net
Bakgrund
När jag en gång lärde mig scope så fick jag aldrig någon bra förklaring till varför jag skulle använda det. Jag fick lära mig på det otrevliga sättet och med bara mig själv som lärare. Därför tänkte jag försöka ge dig, käre läsare, ett par goda anledningar förutom att gå igenom vad det är och innebär. Vi börjar med det senare.
Vad är Scope?
Scope skulle kunna översättas med ”synbarhet” eller ”inriktning” men jag har inte hört något tillräckligt bra ord ännu. Det är i alla fall synbarhet som det hela handlar om och det är framförallt synbarhet mellan olika lager i din kod.För att din kod ska kunna förstås måste den vara överskådlig. För att vara det måste den vara strukturerad. För att vara det måste den vara uppdelad och för att det ska fungera måste du behärska scope.
Private
Synbarhet i kodning i .NET innebär att du kan anropa en funktion, instansiera en klass eller läsa värdet av en variabel. Låt oss börja med den enklaste varianten:
Private Function MyFunction() As String
Dim i As Integer
For i = 0 To 9
'' Gör inget
Next
End Function
I koden ovan har vi två scope. Dels så är funktionen private och sen är variabeln ”i ” enbart ”dimmad” (dimensionerad). Detta innebär att funktionen bara kan ses i samma scope och att ”i” bara är synlig i funktionen. Du kan inte anropa variabeln ”i” utanför funktionen men du kan anropa funktionen om du befinner dig i samma scope. I samma scope kan t ex vara i samma modul eller i samma klass, eller varför inte i samma windowsformulär.
Jag skulle vilja uppehålla mig vid ordet Dim en liten stund och bara säga att det egentligen är en kvarleva från tidigare versioner då ordet Private inte fanns. Den är kvar av gammal vana och betyder egentligen private men kan inte användas som private. Kort och gott: Du kan ersätta Dim med Private men inte tvärt om och Dim kan enbart användas på variabler.
Public
Nästa steg i synlighet är egentligen Friend men det tar jag upp senare. Nu är det dags för Public. Det använder du när du vill att något ska vara helt synligt. Om du deklarerar en klass eller en funktion som public så kommer de att kunna ses igenom alla dina lager och mellan olika projekt. En deklaration av typen public behövs också för att hålla en deklaration av typen Private.Jag försöker exemplifiera:
Public Class Publ
Public Function MyPublicFunction() As String
'' Kod
End Function
Private Class Priv
Private Function MyPrivateFunction() As String
'' Kod
End Function
End Class
End Class
Som du kan se ovan så finns det dels en klass som är public (”Publ”) och dels en klass som är private (”Priv”). Klassen Priv innehåller dessutom en privat funktion (”MyPrivateFunction”).
Om du nu tänker dig att du befinner dig en nivå över denna kod, kanske i ett vanligt windowsformulär och ska anropa mot denna kod så kommer följande att gälla:
- Du kan se klassen Publ.
- Du kan inte se klassen Priv.
- Du kan anropa funktionen MyPublicFunction i klassen Publ.
- Du kan inte anropa funktionen MyPrivateFunction i klassen Priv.
Det är en ganska kass uppställning men det är exempelkod.
Poängen i det hela är att den som skrivit koden vill att anroparen (formuläret) ska kunna använda funktionen MyPublicFunction men inte MyPrivateFunction. Förklaring till detta utlovas senare.
Att du inte rakt av kan definiera en klass, funktion eller variabel som private är egentligen ganska logiskt. Det är ingen mening med det eftersom den då inte kan användas. Det måste alltså ligga något ”runt om” som är synligt utifrån. I fallet ovan är det klassen Publ, som ju är deklarerad som public men den skulle också kunna vara deklarerade som ”Friend”. Det ska jag ta upp nu.
Friend
Friend är något som kommer ifrån den riktiga objektorienteringen och som är ett mellanting mellan private och public. Om man jämför med verkliga livet så är ditt namn kanske Public. Alla som vill kan få känna till det. Något viktigt och lite hemligt om din person, kanske vem du är kär i är något du bara säger till en vän (Friend). Något riktigt hemlig, som du inte vill säga till någon är däremot Private.Tyvärr håller jämförelsen inte hela vägen eftersom man i verkligheten delar mellan varandra om du pratar med en Friend. Så är inte fallet i programmering. Något som deklarerats som Friend delar glatt med sig till alla som vill lyssna utan att vänta sig att få något tillbaka. I praktiken innebär detta att något som är deklarerat som Friend är synligt inom samma projekt som det deklarerade.
Exempelvis:
Friend Class MyFriendClass
Friend SayHello As String = "Tjena vännen!"
End Class
Klassen innehåller bara en variabel (medlem) av strängtyp som hälsar på de som vill kalla klassen sin vän eller helt enkelt anropar den. Om vi nu åter tänker att du befinner dig ett snäpp ”upp” i hierarkin på ett vanligt windowsformulär och denna klass finns i en egen fil i samma projekt så kan MyFriendClass instansieras och SayHello anropas.
Om du däremot befinner dig i ett annat projekt i samma Solution eller på annat sätt har ”länkat in filen” som innehåller MyFriendClass så kommer den inte att vara synlig. Det vill säga att du inte kan instansiera klassen och heller inte anropa SayHello.
Helheten
Som sista förklaring vill jag ge en bild av de olika scope som jag tagit upp och deras förhållande till varandra inom hierarkin.Bilden kan se lite kryptisk ut men du läser den på följande sätt: Ord i fetstil är gränser för synbarhet. En deklaration är enbart synlig inom den del som ligger direkt ovanför i hierarkin, och samtidigt inom de underliggande. Hela bilden kan delas av i mitten mellan de två projekten. För att förtydliga kan vi titta på deklarationen private i ”KlassA”. Den är då synlig inom den gräns som ligger närmast ovanför, vilket är just KlassA. Den kan alltså ses i hela KlassA inklusive FunktionA men inte i ProjektB.
Applicering
Jag har gjort en exempellösning för att riktigt illustrera hur scope fungerar i verkligheten. Det består av två olika projekt och är rikligt dokumenterat och finns att ladda ner Om det skulle strejka (vilket det inte ska) så kan det bero på att MyWin32App-projektet måste ha en referens till MyClasses-projektet. Att lägga till den är inte svårt.
- Högerklicka på ”References” under MyWin32App och välj ”Add reference”.
När dialogen kommit fram välj den tredje fliken, ”projects”, och välj projektet i listan. Klicka därefter på ”Select” och därefter på ”OK”. - När det hela är klart ska din referenslista se ut som följer.
Användning
Så var det då dags för den del som jag lovade skulle komma. Varför är det så bra att kunna scope och vad ska du ha det till.För att riktigt kunna tala om det måste jag introducera ett nytt begrepp: kapsling. Det går ut på att du kapslar in så mycket information du anser nödvändigt och bara visar upp den information som användaren eller anroparen av komponenten behöver.
Om du inte behärskar scope kan du heller inte behärska kapsling.
Kapsling är bra att använda främst av två anledningar, dels för att vissa funktioner och rutiner ska inte kunna anropas utifrån och dels för att bespara din användare från onödig information. Åter igen kan vi titta på ett exempel:
Public Class Validate
Private sqlComm As SqlCommand
Public Function ValidateUser( _
ByVal usr As String, _
ByVal pwd As String) As Boolean
Dim sqlConn As SqlConnection = GetConnection()
sqlComm.Connection = sqlConn
Return ExecuteQuery(sqlComm)
End Function
Private Function GetConnection() _
As SqlConnection
Return New SqlConnection("connstring")
End Function
Private Function ExecuteQuery( _
ByVal com As SqlCommand) As Boolean
If com.ExecuteNonQuery() > 1 Then
Return True
Else
Return False
End If
End Function
End Class
Klassen gör en enkel och kanske inte helt fungerande valideringskontroll för inloggning eller liknande.
Som du kan se finns det tre olika typer av deklarationer. Om klassen skulle instansieras utåt syns bara funktionen ValidateUser. Tänk dig nu hur det skulle se ut om allt var deklarerat som public!
Tänk tillbaka på det jag skrev ovan om att vissa saker ska man inte ens kunna anropa och dels att du vill skydda din anropare från informationsöverflöde. I det första fallet kan anroparen utan problem få fram en koppling till databasen och i det andra fallet finns det nu tre funktioner och inget sätt att veta vilken som måste anropas och i vilken ordning. Hade du fått komponenten i det läget hade du nog lämnat tillbaka med ett ”gör om, gör rätt”.
När du befinner dig i grunderna för att skapa en applikation kanske just inkapsling och scope inte är så viktigt. Det är bara du som behöver hålla reda på allt men så fort det växer. När andra blir involverade och du har mer än ett lager i applikationen så måste du använda inkapsling. Annars kommer det bli en koddjungel utan dess like. Därför bör du tänka i kapslingstermer redan från början.
Sammanfattning
För att sammanfatta kapsling så finns det egentligen bara en tumregel: Exponera aldrig en variabel, funktion o s v längre ut än vad du behöver. Gör det genom en lämplig uppdelning i din kod. Använd scope för att skapa det. Minimera antalet anropsbara funktioner, rutiner m m.Frågor kring eller rättningar i artikeln mottages tacksamt via iMail eller mail. Adressen finns på mitt visitkort.
0 Kommentarer