A Visual Basic programozási nyelv

VB.Net

A .Net technikárol röviden

A .NET technológia lehetővé teszi alkalmazások, programok, szolgáltatások nyelv-, rendszer- és platformfüggetlen felépítését. A nyelvfüggetlenség nemcsak azt jelenti, hogy az alkalmazást bármely .NET nyelven megírhatjuk, hanem azt is, hogy az alkalmazásunk moduljait különböző .NET nyelveken tudjuk megírni, majd ezeket a modulokat könnyen összekapcsolhatjuk egy alkalmazássá.


A .NET vázlatos felépítése

2. Frissítés korábbi VB-ről

Az alábbi elvek betartásával sok fejfájást előzhetünk meg:

3. Főbb különbségek

3.1. Alapértelmezett tulajdonságok

A VB6-ban minden osztálynak lehetett alapértelmezett tulajdonsága. Pl. a TextBox esetében a Text az alapértelmezett tulajdonság.
Legyen két TextBox típusú objektumunk:

Dim a As TextBox
Dim b As TextBox

Ekkor az a = b utasítással a b alapértelmezett tulajdonságát (Text) értékül adja az a alapértelmezett tulajdonságának
Ha azt szeretnénk, hogy az a egy referencia legyen b-re, akkor a következőt kell írni: Set a = b

A VB.NET-ben megszüntették az alapértelmezett tulajdonságot. Az a = b utasítás itt azt jelenti, hogy az a legyen a b egy referenciája.
Ha a b Text tulajdonságát szeretnénk az a Text tulajdonságának értékéül adni, akkor az a.Text = b.Text utasítást kell írnunk (mint egy tetszőleges publikus tulajdonság esetében). Ugyanakkor megmaradt a default property némiképpen változtatott viselkedéssel. Ha van valamilyen belső adatrendszerünk, amelynek elemeit indexekkel lehet egyértelműen azonosítani, akkor használhatjuk. Pl. ha van az osztályunkon belül egy tömb, amiben belső adatokat tárolunk, akkor egy default property-vel elérhetjük, hogy az objektum nevével, és egy indexszel, ennek a tömbnek az elemére hivatkozhassunk. Mivel a property továbbra is a get és set parancsokat hajtja végre, nemcsak számokat adhatunk meg, és nem csak tömböket használhatunk, hanem bármilyen adatszerkezetet, amiben az index alapján azonosíthatunk egy elemet. Az index lehet String is. A set és get procedúráiban pedig magunk valósíthatjuk meg az indexhez az elemek hozzárendelését.

Példa:

Class Elem
Private ePoint(2) as Integer Default Property Point(ByVal poz As Integer) As Integer Get Return ePoint(poz) End Get Set (ByVal Value As Integer) ePoint(poz) = Value

End Set End Property ... End Class

Ekkor megengedett az értékadás és hivatkozás ilyen formája:

Dim a As New Elemek(4, 7) a(0) = 1

3.2. Eljárás- és függvényhívás

Az eljárás- és függvényhívásnál mindig ki kell tenni a zárójelet:

A VB6-ban helyes volt az alábbi eljáráshívas:

foo "bar" call foo("bar")

A VB.NET-ben az alábbi módon helyes:

foo ("bar") call foo("bar")

Tehát a .NET-ben mindig kell zárójel, akkor is, ha nincs visszatérési érték. A call kulcsszó megmaradt, de nincs különösebb jelentősége.

3.3. A static, return kulcsszavak

A static kulcsszó nem kerülhet alprogram neve elé:

Az alábbi kód VB6-ban azt jelentette, hogy az alprogram változói statikusak.

Static Sub foo() Dim x As Integer Dim y As Integer Sub

VB.NET-ben az alábbi módon tudjuk megírni:

Sub foo() Static Dim x As Integer Static Dim y As Integer End Sub

A RETURN kulcsszó:

A Return kulcsszónál egy alprogram rögtön visszatér az őt hívó alprogramhoz. Függvények esetében a visszatérési értéket is át lehet vele adni.

Egy függvényből való visszatérés VB6-ban:

Sub foo() As Integer foo = 42 End Sub

Egy függvényből való visszatérés VB.NET-ben:

Sub foo() As Integer return 42 End Sub

4. Újdonságok

A legfőbb változás, hogy a VB.NET teljesíti az objektum orientált fejlesztőrendszer mind a négy szükséges feltételét:

VB.NET–ben minden változó objektum, még egy egyszerű egész is. Minden objektum a System.Object nevű alapobjektumból származik, örökölve ennek alaptulajdonságait (metódusait stb.). Így például — bár minden objektumnak kell legyen létrehozó (constructor), illetve felszabadító (destructor) metódusa — ezeket nem szükséges megírni saját objektumainkhoz, ha megelégszünk a System.Object alapmetódusaival.

Egyéb újdonságok:

5. Megszűnt elemek

A megszűnt elemek után a teendők olvashatók.

6. Típusok

7. Vezérlők

8. Megszűnő kulcsszavak

9. Objektum orientáltság

9.1. Konstruktorok és destruktorok:

A VB.NET-ben konstruktor váltja az osztályok Initialize eseményét. A működése lényegében azonos, egy fontos kivétellel, hogy a konstruktort nem lehet explicit hívni, és így biztosan csak egyszer fut le. A konstruktor deklarációja Sub New... A program default konstruktort csak akkor hoz létre, ha mi nem hoztunk létre sajátot.

Public Class Square Public Sub New() Console.WriteLine("Square Builder") End Sub End Class Sub Main() Dim sq As Square = New Square End Sub

Természetesen a konstruktor is paraméterezhetők és túlterhelhetők.

Public Class Applicant Public FullName As String Public Sex As String Public DateOfBirth As String ' Az alap konstruktor használata ' Applicant egyed létrejön, de információt nem tartalmaz Public Sub New() FullName = "Unknown" Sex = "N/A" End Sub ' Konstruktor egy paraméterrel Public Sub New(ByVal name As String) FullName = name Sex = "N/A" End Sub ' Konstruktor több paraméterrel Public Sub New(ByVal name As String, ByVal gdr As String, ByVal dob As String) FullName = name Sex = gdr DateOfBirth = dob End Sub End Class

Alapértelmezésben a konstruktorok nem öröklődnek Visual Basicben. Ha az utódosztály konstruktorából meghívjuk az ősosztály konstruktorát, akkor azt még az első sorban kell megtenni. Finalize metódus (destruktor) automatikusan hívódik meg, ha egy objektum elhagyja hatókörét vagy Nothing-ra állítjuk. Ezzel kikényszeríthetjük, hogy a hulladékgyűjtő a következő alkalommal eltávolítsa az objektumot a memóriából. Finalize metódusnál használnunk kell az Overrides kulcsszót, mert tulajdonképpen az Object osztályba beépített Finalize metódust írjuk felül, mivel minden osztály, még ha nem is jelöltük, ennek az Object nevezetű ősosztálynak a leszármazottja.

Egy másik lehetőség az Objektum eltávolítására, ha implementáljuk az IDisposable interface-t ami után a Dispose utasítás segítségével, mikor a felhasználó végzett az objektummal, felszabadíthatjuk az általa foglalt erőforrásokat. Fontos megjegyezni, hogy abban az esetben, ha ezen hívás elmarad, akkor az objektumunk a végtelenségig a memóriában marad - szemét-gyűjtő nem foglalkozik többet vele

Visual Basic lehetőséget ad arra is, hogy a két technikát keverjük. Ha a felhasználónak eszébe jut a munka végezetével meghívni a Dispose-t, akkor ezzel informáljuk a szemét-gyűjtőt hogy végezze el a feladatát (GC.SuppressFinalize()).
Ha elfelejtjük meghívni a Dispose függvényt, de a szemét-gyűjtő úgy véli, hogy befejeztük az objektum használatát, fel tudja szabadítani a tárhelyet

Public Class MyResourceWrapper Implements IDisposable 'A szemét-gyűjtő ezt hívja ' ha a user elfelejtette hívni a Dispose()-t. Protected Overrides Sub Finalize() 'Semmilyen managelt objektumon ne hívj itt Dispose()-t. Try Finally MyBase.Finalize() End Try End Sub 'Ezt a felhasználó tudja hívni Public Sub Dispose() Implements IDisposable.Dispose 'Itt felszabadíthatjuk a nem managelt forrásokat. 'Disposble objektumokon hívhatunk Dispose-t. 'de nem szükséges, ez elvileg úgyis letarol mindent: GC.SuppressFinalize(Me) End Sub End Class

9.2. Az Object ősosztály beépített függvényei és metódusai:

Ha szükségünk van arra, hogy egy értéktípusú változót referenciatípusúként tudjunk kezelni, akkor használhatjuk a bedobozolást.

Dim ertek As Integer = 6 Dim obj As Object = ertek

Ez a funkció akkor is működik ha az Option Strict on-ra van állítva, sőt vannak esetek amikor a háttérben ez történik annak ellenére, hogy mi nem mondjuk meg konkrétan, hogy ezt szeretnénk.

ertek.ToString();

Itt bedobozolódik, és az Object osztály ToString függvénye hívódik meg.

Visszaalakításnál a TypeOf-fal megnézhetjük, hogy az adott típusra visszaalakítható-e, és a DirectCast-tal pedig visszaalakíthatjuk az objektumot.

Dim ertek2 As Integer If TypeOf( obj ) Is Integer Then Ertek2 = DirectCast(obj, Integer) End If

9.3. Névterek:

A névterek segítségével logikailag szétválaszthatjuk a programunkat. Egy névtéren belül alnévteret is definiálhatunk. Vannak beépített névterek pl. a System, de sajátot is létrehozhatunk, az alábbi módon:

Namespace névtérnév ... End Namespace

Ha sokszor kell használnunk egy névtér objektumait és nem szeretnénk mindig kiírni a minősítést, akkor az Imports névtérnév utasítás kiadása után minősítés nélkül hivatkozhatunk a névtér objektumaira. Pl.: Az Imports System.WinForms kiadása után a Dim x As System.WinForms.Button helyett elég a Dim x as Button utasítást írni.

9.4. Öröklődés:

A VB6-ban csak interfész öröklődésre volt lehetőség, de a VB.NET-ben már osztályokból is származtathatunk gyerekosztályokat. Egy osztályt származtatni az Inherits kulcsszóval tudunk pl.:

Public Class Student Inherits Person ... End Class

Ha azt szeretnénk, hogy az osztályunkból már ne lehessen tovább származtatni, akkor azt az alábbi módon kell definiálni:

Public NotInheritable Class Person ... End Class

Ha pedig absztrakt osztályt szeretnénk definiálni, akkor ezt a következő módon tehetjük meg:

Public MustInherit Class Person ... End Class

Ilyenkor a Dim p As New Person hibát okoz.

Egy osztály metódusai és tulajdonságai felüldefiniálhatóak is lehetnek. Ekkor a szülő osztályban Overridable kulcsszóval kell definiálnunk őket, a gyerek osztályban pedig az Overrides kulcsszóval mondjuk meg a fordítónak, hogy felüldefiniálásról van szó. Egy osztálynak lehetnek absztrakt metódusai is, ezeket a MustOverride kulcsszóval vezetjük be. A NotOverridable kulcsszóval jelezzük, ha egy metódust nem lehet felülírni. Ha ennek ellenére mégis felül szeretnénk írni egy metódust, akkor azt elfedhetjük a Shadows kulcsszóval. Azonban ez nem a fenti metódus leszármazottja lesz, hanem egy új metódus. Ha az adott objektum metódusát meghívjuk, akkor az újonnan létrehozott metódus fog lefutni, viszont ha a polimorfizmust használjuk, akkor a régi. Erre láthatunk példát a példaprogramoknál Shadows-zal való elfedés címen.

Ha osztályszintű változókat, függvényeket vagy procedúrákat szeretnénk létrehozni, azt a Shared kulcsszóval tehetjük meg.

Class Ember Private Shared nepesseg As Integer ... End Class

9.5. Me, MyBase, MyClass:

A Me kulcsszóval a metódustörzsekben az adott példányra hivatkozhatunk vele. Más nyelvekben a this, vagy self kulcsszóval egyezik meg.

Az utódosztályban felülírt metódusokban az ősosztályban levő eredeti metódusokat a MyBase kulcsszó segítségével érhetjük el.

Module Module1 Class Ember Private kor As Integer Public Sub New(ByVal ertek As Integer) kor = ertek End Sub End Class Class Gyerek : Inherits Ember Public Sub New(ByVal ertek As Integer) MyBase.New(ertek) End Sub End Class

A MyClass kulcsszóval felülírható metódusokat hívhatunk meg az osztályunkban, miközben biztosítjuk, hogy a metódus felülírt változata helyett a metódusnak az osztálybeli megvalósítása hívódjék meg.

Module Module1 Public Class Allat Sub El() Lelegzes() End Sub Sub El2() MyClass.Lelegzes() End Sub Overridable Sub Lelegzes() Console.WriteLine("Lélegzés...") End Sub End Class Public Class Hal Inherits Allat Overrides Sub Lelegzes() Console.WriteLine("Buborékolás...") End Sub End Class Sub Main() Dim ponty As New Hal() ponty.El() 'Buborékolás... ponty.El2() 'Lélegzés... End Sub End Module

10. Párhuzamosság

10.1 Bevezetés

A .NET keretrendszer és így a Visual Basic .NET is támogatja a szálakat. A System.Threading névtér tartalmaz több különböző eszközt a többszálú alkalmazásokhoz. A Thread osztály természetesen az alap osztály, ami reprezentálja a szálakat. Egy Visual Basic kliens programnak automatikusan egy szál hoz létre a CLR és az operációs rendszer, ezt hívjuk fő szálnak. Többszálú programot további szálak létrehozásával készíthetünk. A Thread osztály alapvetően az System.Object-ből származik, tehát az Equals,Finalize,GetType metódusai használhatók. Egy szál létrehozása a következőképpen történik, először importáljuk a System.Threading csomagot:

Imports System.Threading
Ezután létrehozzuk magát az új szálat:
Dim myThread As New Thread(AddressOf myFunc)
Ahhoz, hogy futassuk a szálat szükségünk van az MS CLR által biztosított ThreadStart delegált osztályt kell használnunk:
myThread.Start()
Lehetőség van arra, hogy az egyik szálat blokkoljuk, amíg egy másik szál nem fut le egyszer a Join() metódussal. Így megoldható szálak szinkronizációja is, például a következő t1 és t2 szál felváltva hajtódik végre, amíg a másik szál még él:
t1.Join() t2.Join()
Tudunk szálat adott ideig várakoztatni Sleep(Int32)-el. A következő szál 1 másodpercig alszik:
t1.Sleep(1000)
Egy szál terminálása:
t1.Abort()
Szál működését fel is tudjuk függeszteni Suspend metódussal. Ha a szál már fel van függesztve nincsen rá hatással. A Resume a felfüggesztett szálat elindítja. Abban az esetben ha szeretnénk informálódni az éppen aktív szálról egyszerűen meg kell hívni a Shared Thread.CurrentThread propertyt.
Shared Sub ExtractExecutingThread() Dim currThread As Thread = Thread.CurrentThread End Sub
Beállíthatunk szálakhoz prioritást is. A prioritás lehet Normal, AboveNormal, BelowNormal, Highest, Lowest. Alapértelmezetten Normal prioritásúak a szálaink, ha nem adunk meg új értéket.
Dim th As New Thread(AddressOf WriteY) th.Priority = ThreadPriority.Lowest
A már blokkolt vagy éppen nem blokkolt szál legközelebbi első blokkolását oldja fel az Interrupt metódus. Az éppen aktuális szálra Thread.CurrentThread-el hivatkozhatunk. Szálak alábbi tulajdonságait adhatjuk vissza:
  1. IsAlive- logikai érték, a szál állapota
  2. IsBackground- logikai érték, ha a szál a háttérben fut, nem akadályozza meg az eljárást a terminálástól.
  3. Name
  4. Priority
  5. ThreadState- IsAlive-nál pontosabban megadja a szál állapotát. Kezdetben minden szál állapota Unstarted.
A System.Threading névtér több egyéb lehetőséget is biztosít a szálak mellett a konkurrens programozási feladatok megvalósítására:
  1. Lehetőség van Interlocked és SyncLock segítségével zárak alkalmazására. Vigyázni kell a holtpontra!
  2. Timer egy metódus adott időközönként való lefuttatására, ez szálakkal is megoldható.
  3. Az erőforráselosztást Semaphore segíti.
  4. Mutex szinkronizációhoz.
  5. Monitor védett objektumok szinkronizációjához.
  6. A Barrier megakadályozza, hogy egy algoritmuson egyszerre több szál is dolgozzon.

10.2 Szinkron hívás, delegate

Ugyan ezt még nem nevezhetjük tényleges párhuzamos programozásnak, ennek ellenére fontos, hogy elmélyüljünk a .NET Delegate-jeiben.
A .NET delegate típus lényegében egy típus-helyes, objektum-orientált, függvény mutató. Amikor deklarálunk egy delegate-t akkor szükségünk lesz - amit a fordító legenerál nekünk - egy NotInheritable osztályra a System.MulticastDelegate-ból származtatva.

' A VB 2010 delegate type. Public Delegate Function BinaryOp(ByVal x As Integer, ByVal y As Integer) As Integer

A BinaryOp bármilyen metódusra rá tud mutatni ami két számot vár, és számot is ad vissza.
A következő osztályt generálja dinamikusan a fordító a delegate-ből:

Public NotInheritable Class BinaryOp Inherits System.MulticastDelegate Public Sub BinaryOp(ByVal target As Object, ByVal functionAddress As UInteger) Public Sub Invoke(ByVal x As Integer, ByVal y As Integer) Public Function BeginInvoke(ByVal x As Integer, ByVal y As Integer, ByVal cb As AsyncCallback, ByVal state As Object) As IAsyncResult Public Function EndInvoke(ByVal result As IAsyncResult) As Integer End Class

Ezek után már csak rá kell mutatnunk az típus-helyes függvényre, majd az Invoke paranccsal szinkron hívást kezdeményezhetünk:

Sub Main() Console.WriteLine("***** Synch Delegate Review *****") ' Print out the ID of the executing thread. Console.WriteLine("Main() invoked on thread {0}.",Thread.CurrentThread.ManagedThreadId) ' Invoke Add() in a synchronous manner. Dim b As New BinaryOp(AddressOf Add) ' Could also write b.Invoke(10, 10); Dim answer As Integer = b(10, 10) ' These lines will not execute until 'the Add() method has completed. Console.WriteLine("Doing more work in Main()!") Console.WriteLine("10 + 10 is {0}.", answer) Console.ReadLine() End Sub Private Function Add(ByVal x As Integer, ByVal y As Integer) As Integer ' Print out the ID of the executing thread. Console.WriteLine("Add() invoked on thread {0}.",Thread.CurrentThread.ManagedThreadId) ' Pause to simulate a lengthy operation. Thread.Sleep(5000) Return x + y End Function

Az eredmény futtatás után:

***** Synch Delegate Review ***** Main() invoked on thread 1. Add() invoked on thread 1. Doing more work in Main()! 10 + 10 is 20. Press any key to continue . . .

10.3 Aszinkron hívás, delegate

A delegate megismerése után, már igen egyszerű dolgunk van.
A BinaryOp osztályunk rendelkezett két eddig nem használt függvénnyel: BeginInvoke, EndInvoke
BeginInvoke-nak átadjuk először a két Integer típusunkat, majd az utolsó két paraméterünk egy System.AsyncCallback és egy System.Object.
Most mindkettőnek Nothing-ot adunk, és az EndInvoke-ra koncentrálunk, aminek a visszatérési értéke Integer típus.

Sub Main() Console.WriteLine("***** Async Delegate Invocation *****") ' Print out the ID of the executing thread. Console.WriteLine("Main() invoked on thread {0}.",Thread.CurrentThread.ManagedThreadId) ' Invoke Add() on a secondary thread. Dim b As New BinaryOp(AddressOf Add) Dim iftAR As IAsyncResult = b.BeginInvoke(10, 10, Nothing,Nothing) ' Do other work on primary thread... Console.WriteLine("Doing more work in Main()!") ' Obtain the result of the Add() ' method when ready. Dim answer As Integer = b.EndInvoke(iftAR) Console.WriteLine("10 + 10 is {0}.", answer) Console.ReadLine() End Sub
***** Async Delegate Invocation ***** Main() invoked on thread 1. Doing more work in Main()! Add() invoked on thread 3. 10 + 10 is 20.

Lehetőségünk van felparaméterezni a BeginInvoke funkciót, egy AsyncCallback metódussal. Ebben az esetben a munka végeztével ez hívódik meg:

Public Delegate Function BinaryOp(ByVal x As Integer, ByVal y As Integer) As Integer Module Module1 Private isDone As Boolean = False Sub Main() Console.WriteLine("***** AsyncCallbackDelegate Example *****") Console.WriteLine("Main() invoked on thread {0}.",Thread.CurrentThread.ManagedThreadId) Dim b As New BinaryOp(AddressOf Add) Dim iftAR As IAsyncResult = b.BeginInvoke(10, 10, New AsyncCallback(AddressOf AddComplete), Nothing) 'Assume other work is performed here... Do While Not isDone Thread.Sleep(1000) Console.WriteLine("Working....") Loop Console.ReadLine() End Sub Private Function Add(ByVal x As Integer, ByVal y As Integer) As Integer Console.WriteLine("Add() invoked on thread {0}.", Thread.CurrentThread.ManagedThreadId) Thread.Sleep(5000) Return x + y End Function Private Sub AddComplete(ByVal itfAR As IAsyncResult) Console.WriteLine("AddComplete() invoked on thread {0}.", Thread.CurrentThread.ManagedThreadId) Console.WriteLine("Your addition is complete") isDone = True End Sub End Module
***** AsyncCallbackDelegate Example ***** Main() invoked on thread 1. Add() invoked on thread 3. Working.... Working.... Working.... Working.... Working.... AddComplete() invoked on thread 3. Your addition is complete

10.4 System.Threading névtér

A .NET keretrendszerből kapott névtér használata tökéletesen analóg minden támogatott programnyelvben, de azért, hogy lássunk a tényleges implementációt, egy példán prezentáljuk működését:

A munkás osztályunk:

Public Class Printer Public Sub PrintNumbers() 'Display Thread info. Console.WriteLine("-> {0} is executing PrintNumbers()",Thread.CurrentThread.Name) ' Print out numbers. Console.Write("Your numbers: ") For i As Integer = 0 To 9 Console.Write("{0}, ", i) Thread.Sleep(2000) Next Console.WriteLine() End Sub End Class

Maga a hívás:

Sub Main() Console.WriteLine("***** The Amazing Thread App *****" & vbLf) Console.Write("Do you want [1] or [2] threads? ") Dim threadCount As String = Console.ReadLine() ' Name the current thread. Dim primaryThread As Thread = Thread.CurrentThread primaryThread.Name = "Primary" 'Display Thread info. Console.WriteLine("-> {0} is executing Main()", Thread.CurrentThread.Name) ' Make worker class. Dim p As New Printer() Select Case threadCount Case "2" ' Now make the thread. Dim backgroundThread As New Thread(New ThreadStart (AddressOf p.PrintNumbers)) backgroundThread.Name = "Secondary" backgroundThread.Start() Case "1" CaseLabel1: p.PrintNumbers() Case Else Console.WriteLine("I don't know what you want...you get 1 thread.") GoTo CaseLabel1 End Select ' Do some additional work. ' MessageBox.Show("I'm busy!", "Work on main thread...") Console.ReadLine() End Sub
***** The Amazing Thread App ***** Do you want [1] or [2] threads? -> Primary is executing Main() -> Secondary is executing PrintNumbers() Your numbers: 0, 1, 2, 3, 4,

11. VB.Net összekapcsolása MySQL -el

Egy jó angol leírás MySQL Connector/NET letöltés

12. Windows Communication Foundation (CodeName: Indigo)

12.1. Bevezetés

A WCF a .NET Framework 3.0-ban jelent meg először.
A WCF segítségével szolgáltatás orientált alkalmazásokat tudunk létrehozni, amelyekkel akár más platformon futó alkalmazásokkal is kommunikálhatunk. Akár Linuxon futó alkalmazásokkal is hatékonyan tud együttműködni. Tehát a WCF nem más, mint egy új generációs technológia elosztott alkalmazások fejlesztéséhez. A WCF egy egységes programozási modellt nyújt a fejlesztők számára. Így ugyanabban a környezetben tudunk fejleszteni, ha webszolgáltatást, enterprise webszolgáltatást vagy épp .NET Remoting alkalmazásokat írunk. Így egy technológia ismeretével a korábbinál kevesebbet kell kódolnunk és hatékonyabban tudunk elosztott alkalmazásokat készíteni. Alapvetően SOA (Service Oriented Architecture) szemléletet követ a WCF, de a SOA -nál többet nyújt. Akár bináris kommunikációra, nyers XML kommunikációra, illetve a P2P kommunikációra is képes. A WCF terminológiában két szereplőről beszélhetünk. Van a szolgáltatás (egy- vagy több végpontot publikál) és van a kliens, amely a szolgáltatások végpontjainak üzeneteket tud küldeni, és a szolgáltatás végpontoktól üzeneteket tud kapni.

WCF architektúra

Minden végpont 3 dolgot fog össze. Az ABC-t. Azaz az Address-t, a Binding-ot , Contract-ot.
De mik is ezek?
Az Address a végpont címe. Ez tulajdonképpen egy URL, amelynek segítségével elérjük az adott szolgáltatást.
A Binding definiálja, hogy milyen módon történik a kommunikáció. (TCP/IP, HTTP stb.. ) Számos előre definiált Bindingot nyújt számunkra a WCF.
A Contract a szolgáltatás interfacenek a leírása, azt határozza meg, hogy az adott szolgáltatás milyen műveleteket támogat. Ez definiálható .NET interfacek formájában vagy WSDL nyelvű leírással is. Azonban mivel átjárás lehetséges a két megoldás között ezért ez lényegtelen számunkra.

12.2. WCF kötések (binding)

A WCF számos beépített kötés típussal rendelkezik, amelyek a legkülönfélébb transzport protokollokat, titkosítást és biztonsági előírásokat valósítanak meg. A leggyakrabban használatos kötések a következők:

12.3. WCF szerződések (contract)

A WCF szerződések meghatározzák az adott szolgáltatás viselkedését. A WCF ötféle szerződést ismer.

Service Contract
Minden WCF osztály implementál legalább egy Service Contractot, amely a szolgáltatás által publikált műveleteket definiálja. A Service Contract egy olyan interface, vagy osztály, amely a [ServiceContract] attribútummal van ellátva.

Operation Contract

A műveletszerződés a szolgáltatás által definiált műveleteket definiálja. Műveletszerződés olyan metódus, amely el van látva egy [OperationContract] attribútummal az inteface-en vagy az osztályon belül.

Metaadatcsere

Ha kész a szolgáltatásunk ezt el kell érnünk a kliensből, de ehhez meg kell kapnia a szerződés leírását. De, hogy kapja meg a szerződés kódját a kliens? Erre több lehetőség is van.
1) Vagy Copy –Paste-tel beilleszthetjük és meghívjuk. Ez egyszerű, de sok esetben lehetetlen feladat hisz mi van akkor ha a szolgáltatás Java platformon fut? Akkor hiába kapnánk meg az interfacet.
2) SOA környezetben természetesen van erre megoldás.
2.1) MetaDataExchange - metaadatcsere cégpont. Egy adott címen letölthetővé válnak a szolgáltatás megvalósításának elemei. Visual Studio 2008-ból az Add Service Reference szolgáltatással használható
2.2) Illetve, ha fejlesztőcsapat másik részét megkérjük, hogy küldje el a számunkra az adott szolgáltatás WSDL vagy XSD leírását.

12.4. WCF szolgáltatás publikálása

De hogy publikáljuk a szolgáltatásunkat? Tulajdonképpen erre sok megoldás létezik. Lehet akár egy console alkalmazás, egy windows forms, egy Windows Service, vagy egy WPF alkalmazás is ami hosztolja. Vagy akár az IIS is hosztolhatja számunkra a szolgáltatást (IIS 5.1, IIS 6.0). Viszont az IIS 5.1 és 6.0 használatával van néhány megkötés a hostolással kapcsolatban. Csak HTTP vagy HTTPS alapú kommunikációt lehet megvalósítani, így csak olyan bindingot használhatunk, ami ezt a kommunikációt valósítja meg. IIS 7.0 alatt már nincsenek ezek a megkötések ott már lehet használni TCP-t vagy bármely más kommunikációt.

12.5. WCF használata

Ahhoz, hogy WCF-et használni tudjuk szükségünk lesz a System.ServiceModel névtérre.
Technikai követelmények:
Operációs Rendszer: Windows Vista, Windows XP SP2 vagy Windows Server 2003 SP1
IDE: Visual Studio 2008 vagy Visual Studio 2005 SP1 with WCF extension
Framework: .NET 3.0 , .NET 3.5
SDK: Microsoft Windows Software Development Kit 6.0
Természetesen minden szoftver INGYENESEN és LEGÁLISAN letölthető az egyetem MSDNAA oldaláról.

13. WCF tutorial

13.1. Szükséges komponensek

Mielőtt nekikezdenénk a feladatnak, szükségünk lesz néhány szoftverre. Ahhoz, hogy a Windows Communication Foundation –t használjuk a következőket kell feltelepíteni.

Ajánlott a Visual Studio 2008, ugyanis teljes értékű támogatással rendelkezik a Windows Communication Foundation-höz. Természetesen minden szoftver INGYENESEN és LEGÁLISAN letölthető az egyetemed MSDNAA oldaláról.

13.2. Demo feladat

A feladatunk annyi lesz, hogy egy olyan kliens és szerveralkalmazást fogunk elkészíteni, amelynél a szerver egy személyi nyilvántartó adatbázis adatait lista, valamint listaelem formájában küldi a kliensnek. Ezt a kliens fogadja, és egyszerű módon kiírja a képernyőre. Így lesz egy olyan alkalmazásunk, melynek segítségével bárhonnan távolról felügyelhetjük, személyi nyilvántartásunk adatait.

13.3. WCF szerver

Legelőször a szerveroldali programot fogjuk elkészíteni:
A szerveralkalmazás fogja az adatokat lekérdezni, és majd ha a kliensnek szüksége van ezekre az adatokra akkor el fogja neki küldeni. Indítsuk el a Visual Studio 2008-at rendszergazdai módban, majd a válasszuk ki a File -> New -> Project… menüpontot és a megjelenő ablakban válasszuk ki a templatek közül a Class Library–t. A neve legyen például: Wcf.Server, majd kattintsunk az OK gombra. Ahhoz, hogy használni tudjuk a WCF-et szükségünk lesz a System.ServiceModel névtérre. Ehhez kattintsunk jobb egérgombbal a Solution Explorerben a References-re és válasszuk ki a megjelenő helyi menüből az Add Reference menüpontot. A megjelenő ablakban a .NET fül alatt keressük ki a System.ServiceModel –t majd jelöljük ki és kattintsunk az OK gombra.
ezzel megvagyunk, adjunk a Solution -ünkhöz egy osztályt. Kattintsunk jobb egérgombbal a Solution Explorerben a project nevén a megjelenő helyi menüben válasszuk ki az Add menüpontot, majd Class… menüpontot. A megjelenő ablakban adjuk meg az osztály nevét, amely most legyen például a DataAccess.vb. Ha ezzel megvagyunk, látunk egy üres osztály, ezt követően adjuk, hozzá a System.ServiceModel névteret az osztályunkhoz ( using System.ServiceModel; ) Az adatelérést biztosító osztályunknak így kell kinéznie.

        Public Class DataAccess
          Private Shared _list As List(Of Person)
          Private Shared Function GetPersonList() As List(Of Person)
            If _list Is Nothing Then
              _list = New List(Of Person)
              With _list
                .Add(New Person(1, "Homer", "Simpson"))
                .Add(New Person(2, "Marge", "Simpson"))
                .Add(New Person(3, "Bart", "Simpson"))
                .Add(New Person(4, "Lisa", "Simpson"))
                .Add(New Person(5, "Maggie", "Simpson"))
              End With
            End If
            Return _list
          End Function

          Public Shared Function GetPerson(ByVal id As Integer) As Person
            Return (From p In GetPersonList() Where p.ID = id).FirstOrDefault
          End Function

          Public Shared Function GetPersons() As List(Of Person)
            Return GetPersonList()
          End Function
        End Class

    
Valós alkalmazásban a fenti osztály egy külön rétegben kap helyet és adatbázishoz kapcsolódik. Következő lépésként szükségünk lesz egy olyan osztályra, mely csak az adattagokat és az azokat beállító, kiolvasó property-ket tartalmazza. Ezt a típusú osztályt szokták úgy is nevezni, hogy DTO, azaz Data Transfer Object Minden típushoz DataContract attribútum, minden propertyhez DataMember attribútum hozzáadása szükséges.
        <DataContract()> _
        Public Class Person
          Private _id As Integer
          <DataMember()> _
          Public Property ID() As Integer
            Get
              Return _id
            End Get
            Set(ByVal value As Integer)
              _id = value
            End Set
          End Property
    
Ezután jön a szolgáltatás szerződés definiálása Az osztályt a megfelelő [ServiceContract] attribútummal kell ellátni, ezáltal létrehozzuk a szolgáltatásszerződést, amely a szolgáltatás által publikált műveleteket definiálja. A GetPerson és a GetPersons metódust pedig egy [OperationContract] attribútummal. Így ez a metódus a szolgáltatás szerződésünk részévé válik. Minden szolgáltatásszerződésnek legalább egy műveletszerződést meg kell valósítania.
        Interfészhez ServiceContract attribútum hozzáadása, névtér cserével: http://tempuri.org
        <ServiceContract(Namespace:="http://demo.wcf.samples")> _
        Public Interface IPerson
          <OperationContract()> _
          Function GetPerson(ByVal id As Integer) As Person
          <OperationContract()> _
          Function GetPersons() As List(Of Person)
        End Interface
        
WCF Service contract implementálása: A service-t megvalósító osztály implementálja az IPerson interfészt. Itt találhatóak meg a szolgáltatáshoz tartozó fizikai műveletek.
        Public Class PersonService
          Implements IPerson

          Public Function GetPerson(ByVal id As Integer) As Person _
              Implements IPerson.GetPerson
            Return DataAccess.GetPerson(id)
          End Function

          Public Function GetPersons() As List(Of Person) _
              Implements IPerson.GetPersons
            Return DataAccess.GetPersons()
          End Function
        End Class
    
Service host alkalmazás készítétése:
Ezt követően hozzunk létre egy a fenti szolgáltatást hostoló alkalmazást. Ez az alkalmazás többféle típus lehet, többnyire IIS webszerver segítségével hostolt webalkalmazás szokott lenni, most azonban az egyszerűség kedvéért legyen egy sima konzol alkalmazás. A solution-höz hozzáadott, újonnan létrehozott konzol alkalmazást fel kell készíteni arra, hogy szolgáltatásként üzemeljen. Legelőször adjuk hozzá az előző projectre mutató referenciát, hogy a konzol alkalmazásból lássuk a szolgáltatás kódját. Majd nyissuk meg a Program.vb –t, és itt is adjuk, hozzá a System.ServiceModel névteret. Ahhoz, hogy publikáljunk egy szolgáltatást és a hozzá tartozó végpontot, három dologra van szükségünk: ABC-re. Azaz Address-re, a Binding-ra, és a Contract-ra. Az address egy egyszerű URI típusú cím, a binding határozza meg a kapcsolatot, amely jelen esetben HTTP alapú lesz, a szerződés meg a műveleteket, amiket majd végrehajthatunk. Most olyan szerveralkalmazást készítünk, ami a helyi gépen fog futni és http-n keresztül kommunikál. Válasszuk a WebHttpBindingot, amely erre a helyzetre tökéletes megoldást kínál. A megvalósításhoz szükségünk lesz egy Uri típusú címre, melynek paraméteréül átadjuk azt a címet, amelyen keresztül majd a szolgáltatásunkat el fogjuk érni. Jelen esetben az url a http://localhost:8080/PersonService lesz. Példányosítanunk kell egy ServiceHost objektumot, amelynek paraméterül át kell adnunk a szerződés osztály típusát, és a címet. Most már kész a szolgáltatás, de még a szolgáltatásnak nincs végpontja, amit publikáljon a külvilág felé, ezért meghívjuk a példányosított ServiceHost AddServicePoint metódusát, amelynek segítségével már publikálhatunk egy szolgáltatás végpontot. Paraméterül pedig adjuk át az ABC –t. Azaz a szerződést, a kötést és a címet. Ha ezzel megvagyunk nincs más dolgunk, mint elindítani a szoláltatást a host.Open() metódus meghívásával. Hogy lássuk, hogy mikor indul el a szolgáltatás, néhány segéd szöveget kiírathatunk magunknak. A Console.ReadLine() -al pedig megakadályozzuk, hogy az alkalmazásunk ne álljon le rögtön. A Program.vb osztálynak tehát így kell kinéznie:
            Dim uri = New Uri("http://localhost:8080/demo/wcf/samples")

            Dim host As New ServiceHost(GetType(PersonService), uri)

            Try

            //Szolgáltatás végpont hozzáadása. WsHttpBinding, TcpBinding, ... Http kérések engedélyezése a kliensnek.

              host.AddServiceEndpoint(GetType(IPerson), New WSHttpBinding, "PersonService")
              Dim behavior As New ServiceMetadataBehavior()
              behavior.HttpGetEnabled = True
              host.Description.Behaviors.Add(behavior)
              Console.WriteLine("Szolgáltatás el fog indulni");
              host.Open();
              Console.WriteLine("Elindult a szolgáltatás");
              Console.WriteLine("Nyomja meg az ENTER-t a leállításához.")
              Console.ReadLine()
              host.Close()
              Catch ex As CommunicationException
              Console.WriteLine("Error: {0}", ex.Message)
              host.Abort()
            End Try
       
Megjegyezzük, hogy a demo projectben nem használtunk konfigurációs beállításokat, hanem minden beállítás kódból történik. Ennek más oldalról történő megközelítése esetén, minden olyan beállítást, mely változhat, konfigurációs állományba, azaz App.config file-be helyezhetünk el. Erre példa az alábbi konfigurációs állomány:
    
            <configuration>
              <system.serviceModel>
                <services>
                  <!-- Megjegyzés: A szolgáltatásnévnek egyeznie kell a szolgáltatásimplementáció konfigurációs nevével. -->
                  <service name="PersonService" behaviorConfiguration="MyServiceTypeBehaviors" >
                    <!-- A következő végpont hozzáadása.  -->
                    <!-- Megjegyzés: A végpont hozzáadásához a szolgáltatásnak http típusú alapcímmel kell rendelkeznie. -->
                    <endpoint contract="IMetadataExchange" binding="mexNamedPipeBinding" address="mex" />
                  </service>
                </services>
                <behaviors>
                  <serviceBehaviors>
                    <behavior name="MyServiceTypeBehaviors" >
                      <!-- A következő elem hozzáadása a szolgáltatás viselkedéskonfigurációjához. -->
                      <serviceMetadata httpGetEnabled="true" httpGetUrl="http://localhost:8080/PersonService" />
                    </behavior>
                  </serviceBehaviors>
                </behaviors>
              </system.serviceModel>
            </configuration>
        
Amit fontos megjegyezni a konfigurációs állományból: A name tag meg kell egyezzen a project névterével, valamint a szolgáltatás szerződés osztály nevével is. Figyeljünk oda, hogy pontosan írjuk be, ugyanis a rendszer a kis és nagybetűk között különbséget tesz. Ha kész a szolgáltatásunk ezt el kell érnünk majd a kliensből, de ezelőtt meg kell hogy kapja a szerződés leírását. Hogy kapja meg a szerződés kódját a kliens? Erre több lehetőség is van, ezek közül a MetaDataExchange a legegyszerűbb. MetaDataExchange - meta adatcsere végpont. Egy adott címen letölthetővé válnak a szolgáltatás megvalósításának elemei. Visual Studio 2008-ból az Add Service Reference szolgáltatással hívható meg. Ez a sor az app.configban azt teszi számunkra lehetővé, hogy a lehető legegyszerűbb módon megkaphassa a kliens a szolgáltatás szerződést.
Ha mindezzel megvagyunk, fordítsuk le az alkalmazásunkat és ha sikeresen lefordul, indítsuk el. Az erdmény az alábbiakhoz hasonló kell legyen.

13.4. WCF kliens

Most, hogy elkészítettük a szerverünket készítsük el, hozzá a kliensalkalmazást is. Indítsuk el a Visual Studio 2008-at rendszergazdai módban, majd a válasszuk ki a File -> New -> Project… menüpontot és a megjelenő ablakban válasszuk ki a templatek közül a Console Application –t. A neve legyen például: Wcf.Client, majd kattintsunk az OK gombra. Ezután magkapjuk a visual studio által legenerált kódvázat. Ha ezzel megvagyunk, kattintsunk jobb egérgombbal a Solution Explorerben a References-re és válasszuk ki a megjelenő helyi menüből az Add Service Reference menüpontot. A megjelenő ablakban adjuk meg azt a címet, amelyen a szolgáltatás interfészét elérhetjük. A cím jelen esetben a http://localhost:8080/PersonService majd kattintsunk a Go nyomógombra. Néhány másodpercen belül, ha a címen megtalálta a szolgáltatást a következő képernyő fogad minket.

Ha ezt látjuk, akkor a Namespacehez adjunk egy tetszőleges választott nevet. Ez lesz a névtere a szolgáltatásunknak. Majd kattintsunk az OK gombra.
Figyelem!
Szerveralkalmazásnak futnia kell, ahhoz, hogy elérjük és lekérjük az adatokat!
Amennyiben idáig eljutottunk, akkor már a kliens eléri a szerver oldalt. Képes hívni a listázó és az elemet visszaadó szerver oldali metódusokat. A tényleges kód, amely ezt elvégzi, nagyon rövid, mindössze néhány sor az egész:

 
        Public Sub Main()
          Dim client As New ServiceReference1.PersonClient()
          Dim pers As ServiceReference1.Person = client.GetPerson(1)
          If Not pers Is Nothing Then
            Console.WriteLine("Person with ID=1 is {0} {1}", _
                              pers.FirstName, pers.LastName)
          End If
          Console.WriteLine("Person list")
              
          For Each p In client.GetPersons()
            Console.WriteLine("{0}: {1} {2}", _
                              p.ID, p.FirstName, p.LastName)
          Next
          Console.ReadLine()
        End Sub
        
A klienshez tartozik egy visual studio által generált konfigurációs állomány is:
    
            <system.serviceModel>
            <bindings>
                <wsHttpBinding>
                    <binding name="WSHttpBinding_IPerson" closeTimeout="00:01:00"
                        openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
                        bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
                        maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
                        messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
                        allowCookies="false">
                        <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                            maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                        <reliableSession ordered="true" inactivityTimeout="00:10:00"
                            enabled="false" />
                        <security mode="Message">
                            <transport clientCredentialType="Windows" proxyCredentialType="None"
                                realm="" />
                            <message clientCredentialType="Windows" negotiateServiceCredential="true"
                                algorithmSuite="Default" establishSecurityContext="true" />
                        </security>
                    </binding>
                </wsHttpBinding>
            </bindings>
            <client>
                <endpoint address="http://localhost:8080/demo/wcf/samples/ServiceLib.PersonService"
                    binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IPerson"
                    contract="ServiceReference1.IPerson" name="WSHttpBinding_IPerson">
                    <identity>
                        <userPrincipalName value="localhost" />
                    </identity>
                </endpoint>
            </client>
        </system.serviceModel>
        
A demo-ban szereplő teljes példa alkalmazás (kliens és szerver is) letölthető az alábbi hivatkozásról: wcfdemo.zip

Több nyelv együttműködése

A .NET technológia lehetővé teszi, hogy különböző nyelveken megírt programot egy más nyelvben felhasználjunk. Például megírunk egy osztályt Visual Basic nyelven, majd később ugyanezt az osztályt használjuk a C# nyelvben. A következő példában egy négyzet osztályt írunk meg Visual Basic nyelven.

    
	'Négyzet osztály
	Public Class Square
		'Oldal attribútum létrehozása
		Private pvt_a As Integer
		Property a() As Integer
			Get
				Return pvt_a
			End Get
			Set(ByVal Value As Integer)
				pvt_a = Value
			End Set
		End Property

		'Konstruktor
		Public Sub New(ByVal value As Integer)
			a = value
		End Sub

		'Kerületet számító metódus
		Public Function Perimeter() As Integer
			Return 4 * a
		End Function

		'Területet számító metódus
		Public Function Area() As Integer
			Return a * a
		End Function

		Public Overrides Function ToString() As String
			Dim text As String
			text = "a: " & a & ", perimeter: " & Perimeter() & ", area: " & Area()
			Return text
		End Function
	End Class
        
Ezután készítsünk egy új C# projektet. A projektben az Add reference segítségével keressük ki az előbb megírt Visual Basic projektet, és adjuk hozzá az exe fájlját. Ezután using VBProjektnév segítségével használhatjuk is az osztályt a C# projektben. A példakódban létrehozunk egy Square példányt és kiíratjuk, valamint létrehozunk egy Rectangle osztályt, amit a VB-ben megírt Square osztályból származtatunk.
    
	using System;
	using System.Collections.Generic;
	using System.Linq;
	using System.Text;
	using VB;	//itt importáljuk be a VB-ben megírt osztályt

	namespace CsharpWithVB
	{
		class Program
		{
			static void Main(string[] args)
			{
				Square square = new Square(5);			//using VB-nek köszönhetően használható
				Console.WriteLine("Square:");
				Console.WriteLine(square.ToString());            
				Rectangle rect = new Rectangle(3, 4);
				Console.WriteLine("Rectangle:");
				Console.WriteLine(rect.ToString());
				Console.ReadLine();
			}
		}

		//Téglalap osztály létrehozása a Square osztályból való származtatással
		class Rectangle : Square
		{
			private int b;
			public Rectangle(int a, int b) : base(a)
			{
				this.b = b;
			}
			//Kerületet számító metódus
			public new int Perimeter()
			{
				return 2 * (a + b);
			}
			//Területet számító metódus
			public new int Area()
			{
				return a * b;
			}
			
			public new string ToString()
			{
				return "a: " + a + ",b: " + b + ", perimeter: " + Perimeter() + ", area: " + Area();
			}
		}
	}
        

Lambda kifejezések

A Visual Studio 2008-as változatától kezdve használhatunk a nyelvben a funkcionális programoknál már jól ismert lambda kifejezéseket. A lambda kifejezés egy név nélküli eljárás vagy függvény, melyet ott használhatunk, ahol a delegáltakat is (Ez alól kivételt képez a RemoveHandler kifejezés). Ezek a kifejezések lehetnek egy- vagy többsorosak is. Lambda kifejezést a Sub vagy Function kulcsszó használatával definiálhatunk, mintha egy szokványos eljárást vagy függvényt definiálnánk. A példában szereplő két lambda kifejezés azonos:

Dim fuggveny1 = Function(x) x + 1
Dim fuggveny2 = Function(x)
Return x + 1
End Function

Az előző példánkban a kifejezést értékül adtuk egy delegate-nek, de lambda függvényt meg is hívhatjuk:

Console.WriteLine((Function(Num As Integer) num + 2)(40))

Mint látható a lambda kifejezések nagyon hasonlóak a nyelvben használt eljárásokhoz és függvényekhez, de számos különbség van köztük:

Nullable típusok

Egy típus akkor nullable, ha értéket adhatunk neki, vagy null-ra állíthatjuk, ami azt jelenti, hogy nincs értéke. Ennél fogva ezzel a típussal egyben azt is kifejezhetjük, hogy adott-e érték. Például a String típus nullable, de az Int32 nem. Egy érték típus nem lehet nullable, mert a mérete akkora, hogy csak a típusnak megfelelő értékeket tárolja, nincs kapacitása a null érték tárolására.

A Nullable struktúra segítségével csak egy érték típust tehetünk nullable-lé, mert a referencia típusok alapból állíthatók null-ra.

Használati esetek

A Nullable típusok olyan dolgokat reprezentálnak, melyek a körülményeknek megfelelően léteznek vagy nem. Például egy HTML tag létezhet egy másik tag-en belül, de nem minden esetben, vagy egy adatbázis valamelyik oszlopa felvehet null értéket, mert az érték hiányát jelöli.

Alapvető tulajdonságok

A Nullable struktúrának két fontos tulajdonsága van, a HasValue és a Value. Ha a Nullable HasValue tulajdonsága igaz, akkor a tárolt érték a Value tulajdonság segítségével kérdezhető le. Ha a HasValue tulajdonság hamis, akkor a Value érték lekérdezése esetén InvalidOperationException kivétel kiváltását eredményezi.

Metódus kiterjesztések

A metódus kiterjesztések segítségével a fejlesztők könnyedén adhatnak saját funkcionalitást olyan adattípusokhoz, melyeket már definiáltak, anélkül hogy származtatniuk kellene. A metódus kiterjesztések lehetővé teszik, hogy olyan metódust írjunk, amely úgy hívódik meg, mintha az osztály adott példányának a metódusa lenne.

A metódus kiterjesztés csak Sub eljárás lehet vagy egy Function függvény. Nem lehet definiálni kiterjesztés tulajdonságot, mezőt vagy eseményt. Minden ilyen eljárást vagy függvényt meg kell jelölni az attribútummal a System.Runtime.CompilerServices névtérből. Az első paraméter definiálja, hogy melyik adattípust szeretnénk kiterjeszteni. Amikor a metódus lefut, az első paraméterhez hozzákötődik az adott példány, mely meghívja a metódust.

Példa

A példában a Print eljárással terjesztjük ki a String osztályt, mely a konzolra írja az adott szöveget:

Imports System.Runtime.CompilerServices
Module StringExtensions


Public Sub Print(ByVal aString As String)
Console.WriteLine(aString)
End Sub

End Module

Kiterjeszthető típusok

Metódus kiterjesztést definiálhatsz a legtöbb típusra, melyet megadhatsz paraméterként is:

Mivel az első paraméter adja meg a kiterjesztett típust, ezért ennek a megadása kötelező, így nem írhatjuk elé az Optional és a ParamArray kulcsszót.

Használati javaslatok

A metódus kiterjesztések segítségével könnyedén terjeszthetünk ki meglévő típusokat. A sikeres használatukhoz azonban néhány dolgot figyelembe kell vennünk. Általánosságban a külső osztályhoz adott kiterjesztések több hibalehetőséget hordoznak magukban, mint a saját magunk által írt osztályokhoz adott kiterjesztések, mivel az objektumok belső viselkedését nem ismerjük.

Precedencia

Amennyiben két kiterjesztett metódus azonos szignatúrával rendelkezik és mindkét kiterjesztés elérhető, akkor a magasabb precedenciájú metódus kerül meghívásra. A következő lista mutatja a precedencia sorrendet, kezdve a legnagyobb értékűvel:

  1. Modulon belül definiált kiterjesztés
  2. Kiterjesztett típusok, melyeket a jelenlegi névtérben lévő adattípusokban definiáltunk, vagy ezek szülei.
  3. Bármilyen típusban definiált kiterjesztés, mely a jelenlegi fájlban importálva van.
  4. Bármilyen névtérben definiált kiterjesztés, mely a jelenlegi fájlban importálva van.
  5. Kiterjesztett metódus, mely bármilyen projekt-szintű importban szerepel.
  6. Kiterjesztett metódus, mely bármely projekt-szintű névtérben szerepel.

Ha a precedencia alapján sem egyértelmű a hívott kiterjesztés, akkor egyértelműen azonosíthatjuk a metódust az őt tartalmazó modullal, például ha a StringExtensions modulból szeretnénk használni a kiterjesztést, akkor használhatjuk a StringExtensions.Print(valami) kifejezést a valami.Print() helyett.

Reflection

A .NET keretrendszer egyik követelménye, hogy a minden terjeszthető kód egységnek (szerelvény) rendelkeznie kell úgynevezett metaadatokkal, melyek jellemzik a szerelvényt és a benne felhasznált típusokat. A Reflection nem más, mint ezeknek a metaadatoknak a vizsgálata. Erre a .NET-ben a System.Reflection névtér szolgáltat eszközöket. Első példánkban nézzük meg, hogyan lehet egy szerelvény minősített nevét lekérdezni:

Dim AssemblyPath As String
AssemblyPath = "C:\Pelda\bin\Szerelveny.dll"
Dim asm As [Assembly] = [Assembly].LoadFrom(AssemblyPath)
Console.WriteLine("Szerelveny neve: " & asm.FullName)

A példánkban az Assembly osztály neve kapcsos zárójelek között szerepel, mivel a Visual Basic .NET nyelvben az Assembly egy kulcsszó is, így viszont osztályként hivatkozunk rá. A példakódban a betöltést a LoadFrom metódussal végezzük, majd a visszatérített Assembly osztály nevét a FullName tulajdonsággal kérdezzük le. Az Assembly számunkra még 3 érdekes metódust is tartalmaz, melynek segítségével különféle szerelvényeket kérhetünk le:

A keretrendszerben a szerelvények között lehetnek függőségek, melynek listáját a GetReferencedAssemblies() metódussal kérdezhetjük le.

A Reflection egyik leghasznosabb felhasználási területe, hogy futás közben információkat tudunk lekérdezni egy szerelvényben lévő típusokról, így akár írhatunk alkalmazásokat, melyek a publikus metódusok alapján dokumentálják szerelvényünket. Ennek a technológiának segítségével futás közben dinamikusan példányosíthatunk osztályokat és meg is hívhatjuk ezeknek az osztályoknak a metódusait.

A System.Type segítségével kérdezhetünk le típus információkat. Egy szerelvényben foglalt típusok listáját lekérdezhetjük az Assembly osztály GetTypes metódusával. Amennyiben egy meglévő típusról szeretnénk típusinformációkat kinyerni, használhatjuk a GetType eljárást:

Dim t1 As Type = GetType(Integer)
Console.WriteLine(t.FullName)