Programjaink fejlesztése során elÅ‘bb-utóbb adódni fog a helyzet, hogy több futtatható fájlban is fel szeretnénk használni ugyanazt a kódrészletet. Ebben az esetben célszerű az ilyen kódrészleteket kiszervezni egy osztálykönyvtárba. Az osztálykönyvtár hasonló az eddig készÃtett futtatható fájljainkhoz, azonban ezek a fájlok nem .exe kiterjesztést fognak kapni, hanem .dll kiterjesztésű fájlokká fordulnak.
Osztálykönyvtárakat készÃthetünk mi magunk is, vagy használhatunk mások által elkészÃtetteket is, illetve használhatunk más .NET nyelven Ãrt osztálykönyvtárat is.
Itt egy pici kitérÅ‘t ejtve megjegyezném, hogy nem minden .dll fájl importálható Visual Studio-ban közvetlenül a projektjeinkbe. Ennek az oka az, hogy a natÃv, gépi kódot tartalmazó programkönyvtárak is .dll kiterjesztést kapnak Windows alatt. Éppen ezért született meg a NuGet csomagkezelÅ‘, amirÅ‘l egy picivel késÅ‘bb lesz majd szó.
Ha saját Osztálykönyvtárat akarunk készÃteni, akkor a Visual Studio-ban egy új projektet kell létrehozni. A projekt létrehozása ablakban pedig a Class Library tÃpust kell kiválasztani.
Ezután ugyanúgy kódolhatunk, mint az eddig megszokott módon. A legnagyobb különbség az lesz, hogy a projektünket nem fogjuk tudni elindÃtani hibakeresÅ‘ (debug) üzemmódban, mivel a .dll fájlok nem futtathatóak. Ez annak köszönhetÅ‘, hogy nincs bennük speciális osztály, metódus definiálva ahol a végrehajtás elkezdÅ‘dne (Main metódus). Ezen projekteket csak egy másik, futtatható projekttel lehet tesztelni.
Ha a projektünkben nincs ilyen, akkor a Solution Explorerben jobb kattintásra feljövÅ‘ menüben választhatjuk azt a lehetÅ‘séget, hogy a projektet betöltjük a C# Interactive ablakba az "Initialize Interactive with project" menüpont segÃtségével.
Az interaktÃv ablakból a könyvtárban definiált tÃpusokat, metódusokat ugyanúgy tudjuk használni, mint egy beépÃtett .NET tÃpust.
Ha pedig futtatható projektben szeretnénk felhasználni az osztálykönyvtárunkat, akkor két lehetőségünk van.
Projekt referencia
Az első és kényelmes megoldás az, hogy ugyanabban a Solution fájlban helyezzük el a futtatható projektünket, mint amelyikben a könyvtár van. Ebben az esetben projekt referenciaként hivatkozhatunk a másik projektre.
Ezt a Solution Explorer-ben a projekten belül a References mappán az Add Reference … menüpont segÃtségével tudjuk megtenni. A már ismerÅ‘s ablakban a Projects menüre kell kattintani, majd kiválasztani a megfelelÅ‘ projektet.
Ennek az elÅ‘nye az, hogy ha módosÃtunk az osztálykönyvtárban, akkor a változások automatikusan láthatóvá válnak a futtatható projekt számára, illetve fordÃtáskor a futtatható fájl mellé másolódik a szükséges .dll is, mivel a hivatkozott dll fájl is le lesz fordÃtva. Ezért a futtatható fájl számára mindig az éppen aktuális verzió lesz elérhetÅ‘.
Fájl referencia
A kicsivel nehezebb út, ha manuálisan, külön projektben hozzuk létre a .dll fájlt és fájl referenciaként adjuk hozzá a projektünkhöz.
Ezt úgy tudjuk megtenni, hogy a korábban ismertetett referencia hozzáadása ablakban a Browse fülre kattintunk, majd kiválasztjuk a szükséges fájlokat.
Ennek a hátránya az, hogy új verzió esetén felül kell Ãrni a fájlt és újra kell fordÃtani az alkalmazásunkat.
További hátrány, hogy Ãgy a hibakeresési és hiba javÃtási lehetÅ‘ségeink igen limitáltak.
Egy projektet alapvetÅ‘en két konfigurációra lehet fordÃtani Visual Studio-ban: csinálhatunk Debug vagy Release fordÃtást.
A Debug, vagyis hibakeresÅ‘ mód az alapértelmezett. Ebben a módban a fordÃtó a legtöbb optimalizációt kihagyja, hogy egy hibakereshetÅ‘ változatot kapjunk. Továbbá a fordÃtás során generálódik minden projekthez egy PDB fájl is, ami a bináris fájl kódot linkeli össze a forráskóddal. Ezért látjuk a forráskódot, ha például Debug módban kapunk egy nem kezelt kivételt.
Ha Release módban fordÃtjuk a programunkat, akkor ezen hibakeresést segÃtÅ‘ dolgok helyett egy végrehajtásra kihegyezett, optimalizált binárist kapunk, ami csak nagyon minimális hibakeresési információval rendelkezik.
Fájl referenciák esetén ezért általában a Release módban fordÃtott .dll fájlokat szokták hozzáadni, hogy kiadáskor ne legyen gond a sebességel, vagy manuálisan a projekt kézi szerkesztésével külön .dll fájlokat húznak be Release és Debug üzemmódban.
Mivel ez igencsak macerás, ezért én a projekt referenciák használatát javaslom, ha kifejezetten nem indokolja speciális eset a fájl referenciák használatát.
Példa
Az alábbi példa a kód oldaláról mutatja meg az osztálykönyvtárak használatát:
using System;
namespace Pelda.Lib
{
//csak a Pelda.Lib.dll fájlon belül látható
//mivel internal
internal static class Belso
{
public const int All = 42;
}
public class PeldaOsztaly
{
public void Hello()
{
Console.WriteLine("Hello. A szamom: {0}", Belso.All);
}
}
}
using Pelda.Lib;
using System;
namespace PeldaClasslib
{
class Program
{
static void Main(string[] args)
{
PeldaOsztaly p = new PeldaOsztaly();
p.Hello();
Console.ReadKey();
}
}
}
A program kimenete:
Hello. A szamom: 42
A példakódban nincsen semmi különleges. Első ránézésre gondolhatnánk azt is, hogy egy bináris fájlban található mind a két osztály, de a névterek nem egyeznek, ami árulkodó jel, hogy itt két binárisról van szó.
Bináris kompatibilitás és kód kompatibilitás
Osztálykönyvtárak esetén két fontos fogalom a bináris és a forráskód kompatibilitás.
A bináris kompatibilitás azt jelenti, hogy az osztálykönyvtárunkat használó program a program újrafordÃtása nélkül képes használni a könyvtárunk új verzióját. Mikor binárisan kompatibilis egy könyvtár?
- A könyvtárban definiált interfészek nem bővültek új tagokkal.
- A könyvtárban definiált osztályok metódusainak szignatúrája, beleértve a konstruktorokat nem változott.
- A könyvtárból nem lett eltávolÃtva vagy átnevezve publikus tag és azok láthatósága nem szűkült.
A forráskód kompatibilitás azt jelenti, hogy az osztálykönyvtárunkat használó program a program újrafordÃtásával képes használni a könyvtárunk új verzióját. Ez egy tágabb kompatibilitást tesz lehetÅ‘vé, mivel a publikált osztályokhoz új tagokat adhatunk hozzá. A forráskód kompatibilitást eltöri:
- Ha változik vagy törlÅ‘dik egy publikus tÃpus neve vagy névtere.
- Ha változik egy publikus metódus szignatúrája.
- Ha egy publikus interfész tagjait bÅ‘vÃtjük.
Amennyiben a könyvtárunk konstansokat exportál ki a publikus interfészén, akkor a konstans értékének a változtatása forráskód kompatibilissá teszi, mivel a konstansok a könyvtárunkat alkalmazó programban a hÃvás helyére behelyettesÃtÅ‘dnek fordÃtási idÅ‘ben. Vagyis a program újrafordÃtása nélkül hiába cseréljük a könyvtárat, mert az alkalmazásba már a régi értékek lettek belefordÃtva és az továbbra is azokat fogja használni.