A .NET a fájlokat byte-ok sorozataként (Stream) kezeli a C++ és számos más objektum orientált programozási nyelvhez hasonlóan.
Ennek előnye a nyelv fejlesztőinek szempontjából, hogy egységesíthető egy ősosztályban a logika és a viselkedés, amit aztán minden, az ősosztályból származó osztály örökölni fog.
Programozói, vagy felhasználói szempontból az előnye ennek az, hogy egyszer kell a keretrendszer, illetve az osztályok viselkedését megismerni. További előny, hogy így modulárisak lehetnek a programjaink, mert nem számít, hogy a fájl vagy adat honnan jön: lemezről, memóriából, vagy hálózatról, esetleg egy tömörített fájlból.
C# esetén az adatfolyamok ősosztálya a Stream. Ennek a lemezen lévő fájlokra specializálódott változata a FileStream. A Stream osztályok byte-ok sorozatával dolgoznak, így a bináris fájlkezelés alapjait valósítják meg. Végső soron minden állomány bináris, de a szöveges fájlok kezelése byte tömbökön keresztül igen problémás és kényelmetlen lenne, mert akkor a programozónak kellene minden egyes alkalommal leimplementálnia a különböző karakterkódolások kezelését és egyéb szépségeket.
Éppen ezért a keretrendszer tartalmaz számos Writer osztályt, amelyek a stream-ekre épülnek és kényelmes adatfolyam kezelést tesznek lehetővé.
A Stream maga egy absztrakt osztály. A következő fontos tulajdonságokkal és metódusokkal rendelkezik:
public bool CanRead { get; }
A leszármaztatott adatfolyamról megmondja, hogy olvasható-e.
public bool CanSeek { get; }
A leszármaztatott adatfolyamról megmondja, hogy az olvasási, illetve írási pozíció szabadon módosítható-e. Ha az értéke false, akkor az adatfolyam csak az elejétől a végéig lineárisan olvasható (Pl. hálózati letöltés, kitömörítés)
public bool CanWrite { get; }
A leszármaztatott adatfolyamról megmondja, hogy írható-e.
public bool CanTimeout { get; }
A leszármaztatott adatfolyamról megmondja, hogy dobhat-e TimeOutException kivételt. Bizonyos stream-ek olvasásánál, írásánál előfordulhat, hogy a cél/forrás eszköz túl lassú és időigényes a művelet, ezért nem azonnal történnek meg a dolgok. Ekkor azonnal nem dob kivételt a rendszer, csak akkor ha a művelet egy meghatározott időn belül sem ért véget. Ilyen adatfolyamok például a hálózatról érkező adatok kezelésénél használatosak.
public long Length { get; }
Ha a leszármaztatott adatfolyam támogatja, akkor ezzel kérdezhető le az adatfolyam hossza. Visszatérési értéke byte-ban értendő.
public long Position { get; set; }
Ha a leszármaztatott adatfolyam támogatja, akkor ezzel kérdezhető le és állítható be az aktuális írási és olvasási pozíció.
public int ReadTimeout { get; set; }
public int WriteTimeout { get; set; }
Ha a leszármaztatott adatfolyam támogatja, akkor ezekkel a tulajdonságokkal szabályozható az olvasási és írási timeout.
public int ReadByte ();
public void WriteByte (byte value);
Egyetlen egy byte olvasása, vagy írása az adatfolyamba. Használata nem ajánlott, mert lassú tud lenni.
public void Close ();
public void Dispose ();
A stream felszabadítása. Minden stream implementálja az IDisposable interfészt és a leszármaztatott osztályok ennek a működését egészítik ki. A Close lényegében ugyanazt csinálja, mint a Dispose().
Belsőleg a Dispose implementációra azért van szükség, mert a stream keretrendszer Windows esetén (és más operációs rendszerek esetén is) az operációs rendszer belső függvényeit használja azok összes előnyével és hátrányával együtt. Előnye, hogy a stream-ek gyorsítótárazását és annak optimalizálását elvégzi az operációs rendszer, cserébe viszont natív kódot használ, amit magunknak kell felszabadítani. Arról nem is beszélve, hogy a nemdeterminisztikus szemétgyűjtő (GC) miatt előfordulhatna, hogy két hosszabb írási és olvasási művelet között bezáródhatna a fájl. Éppen ezért a Stream osztály leszármazottjait using blokkon keresztül szoktuk használni.
public void CopyTo (System.IO.Stream destination);
public void CopyTo (System.IO.Stream destination, int bufferSize);
A stream teljes tartalmát átmásolja a paraméterként megadott stream-be. A második paramétere a buffer méretét szabályozza. Alapértelmezetten a buffer méret 81920 byte, ami 80KiB (Kibibyte)
public void Flush ();
A stream belső bufferében lévő adatok írása a stream-re, majd a buffer ürítése.
public long Seek (long offset, System.IO.SeekOrigin origin);
Írási és olvasási pozíció szabályozása. Az első paraméter a léptetést határozza meg az referencia pozíciótól kezdve. Második paramétere a referencia pontot szabályozza. A SeekOrigin egy felsorolás, ami az alábbi értékeket veheti fel:
| Érték | Hatása |
|---|---|
| SeekOrigin.Begin | Pozícionálás a stream elejétől |
| SeekOrigin.Current | Pozícionálás az aktuális pozíciótól |
| SeekOrigin.End | Pozícionálás a stream végétől |
A metódus visszatérési értéke a művelet végén az abszolút pozíció lesz a stream-en belül. A léptetés értéke lehet negatív is. Kivétel fog keletkezni, ha a fájl méretein kívül szaladunk pozícionálás során.
public void Write (byte[] buffer, int offset, int count);
Bufferelt írás. Az első paraméter a kiírandó buffer byte tömböt határozza meg. A második paramétere a bufferben az első kiírandó elem indexét határozza meg. A harmadik paraméter pedig a kiírandó elemek számát befolyásolja.
public abstract int Read (byte[] buffer, int offset, int count);
Bufferelt olvasás. Első paramétere az olvasás célját biztosító byte tömb. A második paramétere a tömbben határozza meg, hogy melyik indextől kezdődjön az elemek elhelyezése. Harmadik paramétere pedig a beolvasandó elemek számát befolyásolja.