日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

溫故而知新:Stream篇

 abin30 2019-03-19

  目錄:

什么是Stream?

什么是字節(jié)序列?

Stream的構(gòu)造函數(shù)

Stream的重要屬性及方法

Stream的示例

Stream異步讀寫

Stream 和其子類的類圖

本章總結(jié)

什么是Stream?

MSDN 中的解釋太簡(jiǎn)潔了: 提供字節(jié)序列的一般視圖

(我可不想這么理解,這必定讓我抓狂,我理解的流是向自然界的河流那樣清澈而又美麗,c#中的流也是一樣,許多技術(shù)或者說核心技術(shù)都需要流的幫忙)

那什么是字節(jié)序列呢?

其實(shí)簡(jiǎn)單的來理解的話字節(jié)序列指的是:

字節(jié)對(duì)象都被存儲(chǔ)為連續(xù)的字節(jié)序列,字節(jié)按照一定的順序進(jìn)行排序組成了字節(jié)序列

那什么關(guān)于流的解釋可以抽象為下列情況:

打個(gè)比方:一條河中有一條魚游過,這個(gè)魚就是一個(gè)字節(jié),這個(gè)字節(jié)包括魚的眼睛,嘴巴,等組成8個(gè)二進(jìn)制,顯然這條河就是我們的核心對(duì)象:流

馬上進(jìn)入正題,讓我們來解釋下c#的 Stream 是如何使用的

讓我們直接溫故或?qū)W習(xí)下Stream類的結(jié)構(gòu),屬性和相關(guān)方法

首先是構(gòu)造函數(shù)

Stream 類有一個(gè)protected 類型的構(gòu)造函數(shù), 但是它是個(gè)抽象類,無(wú)法直接如下使用

   Stream stream = new Stream();

所以我們自定義一個(gè)流繼承自Stream 看看哪些屬性必須重寫或自定義:

View Code

可以看出系統(tǒng)自動(dòng)幫我們實(shí)現(xiàn)了Stream 的抽象屬性和屬性方法

   1:  CanRead: 只讀屬性,判斷該流是否能夠讀?。?/p>

   2:  CanSeek: 只讀屬性,判斷該流是否支持跟蹤查找

   3:  CanWrite: 只讀屬性,判斷當(dāng)前流是否可寫

*4: void Flush():這點(diǎn)必須說得仔細(xì)些:

    當(dāng)我們使用流寫文件時(shí),數(shù)據(jù)流會(huì)先進(jìn)入到緩沖區(qū)中,而不會(huì)立刻寫入文件,當(dāng)執(zhí)行這個(gè)方法后,緩沖區(qū)的數(shù)據(jù)流會(huì)立即注入基礎(chǔ)流

     MSDN中的描述:使用此方法將所有信息從基礎(chǔ)緩沖區(qū)移動(dòng)到其目標(biāo)或清除緩沖區(qū),或者同時(shí)執(zhí)行這兩種操作。根據(jù)對(duì)象的狀態(tài),可能需要修

     改流內(nèi)的當(dāng)前位置(例如,在基礎(chǔ)流支持查找的情況下即如此)當(dāng)使用 StreamWriterBinaryWriter 類時(shí),不要刷新 Stream 基對(duì)象。

     而應(yīng)使用該類的 FlushClose 方法,此方法確保首先將該數(shù)據(jù)刷新至基礎(chǔ)流,然后再將其寫入文件。

(紅色部分為關(guān)鍵請(qǐng)大家務(wù)必能夠理解,今后會(huì)在相應(yīng)的章節(jié)中介紹)

  5: Length:表示流的長(zhǎng)度

*6: Position屬性:(非常重要

雖然從字面中可以看出這個(gè)Position屬性只是標(biāo)示了流中的一個(gè)位置而已,可是我們?cè)趯?shí)際開發(fā)中會(huì)發(fā)現(xiàn)這個(gè)想法會(huì)非常的幼稚,

很多asp.net項(xiàng)目中文件或圖片上傳中很多朋友會(huì)經(jīng)歷過這樣一個(gè)痛苦:Stream對(duì)象被緩存了,導(dǎo)致了Position屬性在流中無(wú)法

找到正確的位置,這點(diǎn)會(huì)讓人抓狂,其實(shí)解決這個(gè)問題很簡(jiǎn)單,聰明的你肯定想到了,其實(shí)我們每次使用流前必須將Stream.Position

設(shè)置成0就行了,但是這還不能根本上解決問題,最好的方法就是用Using語(yǔ)句將流對(duì)象包裹起來,用完后關(guān)閉回收即可。

*7: abstract int Read(byte[] buffer, int offset, int count)

這個(gè)方法包含了3個(gè)關(guān)鍵的參數(shù):緩沖字節(jié)數(shù)組,位移偏量和讀取字節(jié)個(gè)數(shù),每次讀取一個(gè)字節(jié)后會(huì)返回一個(gè)緩沖區(qū)中的總字節(jié)數(shù)

第一個(gè)參數(shù):這個(gè)數(shù)組相當(dāng)于一個(gè)空盒子,Read()方法每次讀取流中的一個(gè)字節(jié)將其放進(jìn)這個(gè)空盒子中。(全部讀完后便可使用buffer字節(jié)數(shù)組了

第二個(gè)參數(shù):表示位移偏量,告訴我們從流中哪個(gè)位置(偏移量)開始讀取。

最后一個(gè)參數(shù):就是讀取多少字節(jié)數(shù)。

返回值便是總共讀取了多少字節(jié)數(shù).

*8: abstract long Seek(long offset, SeekOrigin origin)

    大家還記得Position屬性么?其實(shí)Seek方法就是重新設(shè)定流中的一個(gè)位置,在說明offset參數(shù)作用之前大家先來了解下SeekOrigin這個(gè)枚舉:

如果 offset 為負(fù),則要求新位置位于 origin 指定的位置之前,其間隔相差 offset 指定的字節(jié)數(shù)。如果 offset 為零 (0),則要求新位置位于由 origin 指定的位置處。

如果 offset 為正,則要求新位置位于 origin 指定的位置之后,其間隔相差 offset 指定的字節(jié)數(shù).

Stream. Seek(-3,Origin.End);  表示在流末端往前數(shù)第3個(gè)位置

Stream. Seek(0,Origin.Begin); 表示在流的開頭位置

Stream. Seek(3,Orig`in.Current); 表示在流的當(dāng)前位置往后數(shù)第三個(gè)位置

查找之后會(huì)返回一個(gè)流中的一個(gè)新位置。其實(shí)說道這大家就能理解Seek方法的精妙之處了吧

*9: abstract void Write(byte[] buffer,int offset,int count)

這個(gè)方法包含了3個(gè)關(guān)鍵的參數(shù):緩沖字節(jié)數(shù)組,位移偏量和讀取字節(jié)個(gè)數(shù)

和read方法不同的是 write方法中的第一個(gè)參數(shù)buffer已經(jīng)有了許多byte類型

的數(shù)據(jù),我們只需通過設(shè)置 offset和count來將buffer中的數(shù)據(jù)寫入流中

*10: virtual void Close()

關(guān)閉流并釋放資源,在實(shí)際操作中,如果不用using的話,別忘了使用完流之后將其關(guān)閉

這個(gè)方法特別重要,使用完當(dāng)前流千萬(wàn)別忘記關(guān)閉!

 

為了讓大家能夠快速理解和消化上述屬性和方法我會(huì)寫個(gè)示例并且關(guān)鍵部分會(huì)詳細(xì)說明

復(fù)制代碼
    static void Main(string[] args)
{
byte[] buffer = null;

string testString = "Stream!Hello world";
char[] readCharArray = null;
byte[] readBuffer = null;
string readString = string.Empty;
//關(guān)于MemoryStream 我會(huì)在后續(xù)章節(jié)詳細(xì)闡述
using (MemoryStream stream = new MemoryStream())
{
Console.WriteLine("初始字符串為:{0}", testString);
//如果該流可寫
if (stream.CanWrite)
{
//首先我們嘗試將testString寫入流中
//關(guān)于Encoding我會(huì)在另一篇文章中詳細(xì)說明,暫且通過它實(shí)現(xiàn)string->byte[]的轉(zhuǎn)換
buffer = Encoding.Default.GetBytes(testString);
//我們從該數(shù)組的第一個(gè)位置開始寫,長(zhǎng)度為3,寫完之后 stream中便有了數(shù)據(jù)
//對(duì)于新手來說很難理解的就是數(shù)據(jù)是什么時(shí)候?qū)懭氲搅髦?,在冗長(zhǎng)的項(xiàng)目代碼面前,我碰見過很
//多新手都會(huì)有這種經(jīng)歷,我希望能夠用如此簡(jiǎn)單的代碼讓新手或者老手們?cè)跍毓氏禄A(chǔ)
stream.Write(buffer, 0,3);

Console.WriteLine("現(xiàn)在Stream.Postion在第{0}位置",stream.Position+1);

//從剛才結(jié)束的位置(當(dāng)前位置)往后移3位,到第7位
long newPositionInStream =stream.CanSeek? stream.Seek(3, SeekOrigin.Current):0;

Console.WriteLine("重新定位后Stream.Postion在第{0}位置", newPositionInStream+1);
if (newPositionInStream < buffer.Length)
{
//將從新位置(第7位)一直寫到buffer的末尾,注意下stream已經(jīng)寫入了3個(gè)數(shù)據(jù)“Str”
stream.Write(buffer, (int)newPositionInStream, buffer.Length - (int)newPositionInStream);
}


//寫完后將stream的Position屬性設(shè)置成0,開始讀流中的數(shù)據(jù)
stream.Position = 0;

// 設(shè)置一個(gè)空的盒子來接收流中的數(shù)據(jù),長(zhǎng)度根據(jù)stream的長(zhǎng)度來決定
readBuffer = new byte[stream.Length];


//設(shè)置stream總的讀取數(shù)量 ,
//注意!這時(shí)候流已經(jīng)把數(shù)據(jù)讀到了readBuffer中
int count = stream.CanRead?stream.Read(readBuffer, 0, readBuffer.Length):0;


//由于剛開始時(shí)我們使用加密Encoding的方式,所以我們必須解密將readBuffer轉(zhuǎn)化成Char數(shù)組,這樣才能重新拼接成string

//首先通過流讀出的readBuffer的數(shù)據(jù)求出從相應(yīng)Char的數(shù)量
int charCount = Encoding.Default.GetCharCount(readBuffer, 0, count);
//通過該Char的數(shù)量 設(shè)定一個(gè)新的readCharArray數(shù)組
readCharArray = new char[charCount];
//Encoding 類的強(qiáng)悍之處就是不僅包含加密的方法,甚至將解密者都能創(chuàng)建出來(GetDecoder()),
//解密者便會(huì)將readCharArray填充(通過GetChars方法,把readBuffer 逐個(gè)轉(zhuǎn)化將byte轉(zhuǎn)化成char,并且按一致順序填充到readCharArray中)
Encoding.Default.GetDecoder().GetChars(readBuffer, 0, count, readCharArray, 0);
for (int i = 0; i < readCharArray.Length; i++)
{
readString += readCharArray[i];
}
Console.WriteLine("讀取的字符串為:{0}", readString);
}

stream.Close();
}

Console.ReadLine();

}
復(fù)制代碼


顯示結(jié)果:

大家需要特別注意的是stream.Positon這個(gè)很神奇的屬性,在復(fù)雜的程序中,往往流對(duì)象操作也會(huì)很復(fù)雜,

一定要切記將stream.Positon設(shè)置在你所需要的正確位置,還有就是 using語(yǔ)句的使用,它會(huì)自動(dòng)銷毀stream對(duì)象,

當(dāng)然Stream.Close()大家都懂的

 

接著讓我們來說下關(guān)于流中怎么實(shí)現(xiàn)異步操作

在Stream基類中還有幾個(gè)關(guān)鍵方法,它們能夠很好實(shí)現(xiàn)異步的讀寫,

復(fù)制代碼
異步讀取
public virtual IAsyncResult BeginRead(byte[] buffer,int offset,int count,AsyncCallback callback,Object state)
異步寫
public virtual IAsyncResult BeginWrite( byte[] buffer, int offset, int count, AsyncCallback callback, Object state )
結(jié)束異步讀取
public virtual int EndRead( IAsyncResult asyncResult ) 
結(jié)束異步寫
public virtual void EndWrite( IAsyncResult asyncResult )  
復(fù)制代碼

大家很容易的就能發(fā)現(xiàn)前兩個(gè)方法實(shí)現(xiàn)了IAsyncResult接口,后2個(gè)end方法也順應(yīng)帶上了一個(gè)IAsyncResult參數(shù),

其實(shí)并不復(fù)雜,(必須說明下 每次調(diào)用 Begin方法時(shí)都必須調(diào)用一次 相對(duì)應(yīng)的end方法)

和一般同步read或write方法一致的是,他們可以當(dāng)做同步方法使用,但是在復(fù)雜的情況下可能也難逃阻塞崩潰等等,但是一旦啟用了

異步之后,這些類似于阻塞問題會(huì)不復(fù)存在,可見微軟對(duì)于異步的支持正在加大。

 最后是有關(guān)c#中Stream類和其子類的類圖

  類圖呢?大家肯定會(huì)這么想把 ^^

   為什么這個(gè)在目錄中是灰色的?其實(shí)我個(gè)人覺得這個(gè)類圖不應(yīng)該放在這篇博文中,原因是我們真正理解并熟練操作了Stream的所有子類?(大牛除外)

  (這也是我寫后續(xù)文章的動(dòng)力之一,寫博能很好的提升知識(shí)點(diǎn)的吸收,不僅能幫助別人,也能提高自己的對(duì)于知識(shí)點(diǎn)的理解),所以我想把類圖放在這

   個(gè)系類的總結(jié)篇中

本章總結(jié):

本章介紹了流的基本概念和c#中關(guān)于流的基類Stream所包含的一些重要的屬性和方法,關(guān)鍵是一些方法和屬性的細(xì)節(jié)和我們操作流對(duì)象時(shí)必須注意的事項(xiàng),

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多