• C# 温故而知新:Stream篇


    C# 温故而知新:Stream篇(

    BufferedStream

    目录:

    1 简单介绍一下BufferedStream

    在前几章的讲述中,我们已经能够掌握流的基本特性和特点,一般进行对流的处理时系统肩负着IO所带来的开销,调用十分频繁

    这时候就应该想个办法去减少这种开销,而且必须在已有Stream进行扩展,有了以上2点需求,那么我们今天的主题,

    BufferedStream闪亮登场了,BufferedStream能够实现流的缓存,换句话说也就是在内存中能够缓存一定的数据而不是

    时时给系统带来负担,同时BufferedStream可以对缓存中的数据进行写入或是读取,所以对流的性能带来一定的提升

    但是无法同时进行读取或写入工作,如果不使用缓冲区也行,BufferedStream能够保证不用缓冲区时不会降低因缓冲区带来

    的读取或写入性能的下降

    2 如何理解缓冲区

    缓冲区是内存中的一块连续区域,用来缓存或临时存储数据,也就是说流可以通过缓冲区逐步对数据进行读取或写入操作,

    BufferedStream 中的缓存区可以由用户设定,其表现形式为byte数组,想象下没有缓存区将是很可怕的,假如我们的

    非固态硬盘没有缓冲区,如果我们下载速度达到惊人的10m左右,那么下载一个2G或更大的文件时,磁头的读写是非常

    的频繁,直接的结果是磁头寿命急剧减少,甚至将硬盘直接烧毁或者损坏

    3 BufferedStream的优势

    理解了缓冲区的重要性后,让我们在来谈下BufferedStream的优势,首先大家肯定觉的疑惑为什么MemoryStream 同样

    也是在内存中对流进行操作,和BufferedStream有什么区别呢?BufferedStream并不是将所有内容都存放到内存中

    而MemoryStream则是BufferedStream必须跟其他流如FileStream结合使用,而MemoryStream则不用,聪明的你

    肯定能够想到,BufferedStream必然类似于一个流的包装类,对流进行”缓存功能的扩展包装”,所以BufferedStream的

    优势不仅体现在其原有的缓存功能上,更体现在如何帮助原有类实现其功能的扩展层面上

    4 从BufferedStream 中简单学习下装饰模式

    如何理解装饰模式

                 我们在做项目时或者设计项目时常常会碰到这个问题 :我们该如何扩展已有的类功能或者如果扩展一系列派生类的

                 功能呢,可能你立刻会想到继承,的确不错,但是如果你仔细看下图并且展开一定的想象的话,你就会发现继承可能

                 导致子类的膨胀性增加,如下图所示

               

    首先还是得注意以下原则

    1. 多用组合,少用继承

    利用继承设计子类的行为,是在编译时静态决定的,而且所有的子类都会继承到相同的行为。然而,如果能够利用组合的做法扩展对象的行为,就可以在运行时动态地进行扩展。

    2. 类应设计的对扩展开放,对修改关闭。

    那么我们该如何避免子类的扩张同时又实现Girl类原有类或派生类的新功能呢?

    首先我们要达到2个目的:

    1 能够为Girl的所有派生类都实现新功能(不修改派生类的结构)

    2 利用对象组合的方式

    为了满足为Girl 类所有派生类都能使用,那么我们就加上一个Girl的装饰类GirlWrapper:

    复制代码
      public abstract class GirlWrapper : Girl
        {
            protected Girl girl;
    
            public GirlWrapper(Girl thisGril)
            {
                this.girl = thisGril;
            }
            public override void Decrorator()
            {
                girl.Decrorator();
            }
            public override string ToString()
            {
                return string.Format("{0}:{1}", this.girl.GirlName, this.girl.Nation);
            }
        }
    复制代码

    该类继承了Girl类,从而保证了和其他派生类有共同的基本结构,

    既然有了这个装饰类,那我们便可以删掉原来的Singing 接口,添加一个

    SingingGirlWrapper类来实现对girl的包装类,

    复制代码
        public class SingingGirlWrapper : GirlWrapper
        {
            public SingingGirlWrapper(Girl thisGril)
                : base(thisGril)
            {
    
            }
            public void Decorator() 
            {
                Console.WriteLine("SingingGirlWrapper decorateor:The girl named {0} who from {1} is {2} can singing nao", 
                    this.GirlName, this.Nation, this.girl.GetType().Name);
                base.Decrorator();
            }
    
            public override string ToString()
            {
                return base.ToString();
            }
        }
    复制代码

               大家不必拘泥于派生的包装类,你完全可以建立一个新的girl包装类来实现特定的功能,上述例子只是演示下派生的包装类
              这样的话,我们便使用了组合的方式实现了既保留原有的接口(或者抽象类),又动态添加了新功能

              

    在使用时我们可以将派生类的对象放入装饰类的构造中,这样的话,在执行包装类Decorator方法时,可以执行被包装对象的

    Decorator方法和包装类的Decorator方法从而实现对Girl派生类的包装,这样的话就能实现灵活的组合扩展。

    复制代码
    static void Main(string[] args)
            {
                Queen queen = new Queen("Mary","Unite States");
                SingingGirlWrapper sgw = new SingingGirlWrapper(queen);
                sgw.Decorator();
                Console.ReadLine();
            }
    复制代码


                  再次理解下装饰模式在Stream中的作用

    通过以上的例子在回到BufferStream章节中,大家肯定一眼就看出了BufferStream其实就是上述例子中的wrapper类

    而Stream 类就是其共同的父类,为了给所有的流类提供缓冲功能所以BufferedStream便诞生了,这样的话,我们可以

    不用修改其派生类结构,便能灵活组合将缓冲功能嵌入stream中

    5 BufferedStream的构造

    BufferedStream(Stream)

    其实BufferedStream的构造主要功能还是设置缓冲区大小,如果没有指定则默认是用4096字节的进行初始化

    BufferedStream(Stream, Int32)

    第二个参数是手动指定缓冲区大小

    第一次使用此构造函数初始化 BufferedStream 对象时分配共享读/写缓冲区。 如果所有的读和写都大于或等于缓冲区大小,则不使用共享缓冲区

    6 BufferedStream的属性

    *1 CanRead 已重写。获取一个值,该值指示当前流是否支持读取。 
    如果流支持读取,则为 true;如果流已关闭或是通过只写访问方式打开的,则为 false。 
    如果从 Stream 派生的类不支持读取,则对 StreamReader、StringReader、TextReader 的 Read、ReadByte、BeginRead、EndRead 和 Peek 方法的调用将引发 NotSupportedException。 
    如果该流已关闭,此属性将返回 false。

     

    *2 CanSeek 已重写。获取一个值,该值指示当前流是否支持查找。 
    如果流支持查找,则为 true;如果流已关闭或者如果流是由操作系统句柄(如管道或到控制台的输出)构造的,则为 false。 
    如果从 Stream 派生的类不支持查找,则对 Length、SetLength、Position 和 Seek 的调用将引发 NotSupportedException。 
    如果该流已关闭,此属性将返回 false。

     

    *3  CanWrite 已重写。获取一个值,该值指示当前流是否支持写入。 
    如果流支持写入,则为 true;如果流已关闭或是通过只读访问方式打开的,则为 false。 如果从 Stream 派生的类不支持写入,

    则调用 SetLength、Write 或 WriteByte 将引发 NotSupportedException。 如果该流已关闭,此属性将返回 false。

     

    *4  Length 已重写。获取流长度,长度以字节为单位。

     

    *5  Position 已重写。获取当前流内的位置。

     get 访问器调用 Seek 获取基础流中的当前位置,然后根据缓冲区中的当前位置调整此值

     set 访问器将以前写入缓冲区的所有数据都复制到基础流中,然后调用 Seek

     支持搜索到超出流长度的任何位置。

     

    7 BufferedStream的方法

    BufferStream的方法基本上和Stream类一致,没有其独特的方法

    关于以上方法的注意事项的大家也可参考我的第一篇

     简单示例:利用socket 读取网页并保存在本地

    复制代码
        class Program
        {
            static void Main(string[] args)
            {
                Server server = new Server("http://www.163.com/");
                server.FetchWebPageData();
            }
        }
    
        public class Server
        {
            //端口
            const int webPort = 80;
            //默认接收缓存大小
            byte[] receiveBufferBytes = new byte[4096];
            //需要获取网页的url
            private  string webPageURL;
            public Server(string webPageUrl)
            {
                webPageURL = webPageUrl;
            }
    
           /// <summary>
            ///  从该网页上获取数据
           /// </summary>
            public void FetchWebPageData() 
            {
                if (!string.IsNullOrEmpty(webPageURL))
                FetchWebPageData(webPageURL);
                Console.ReadLine();
            }
    
            /// <summary>
            /// 从该网页上获取数据
            /// </summary>
            /// <param name="webPageURL">网页url</param>
            private void FetchWebPageData(string webPageURL) 
            {
                //通过url获取主机信息
                IPHostEntry iphe = Dns.GetHostEntry(GetHostNameBystrUrl(webPageURL));
                Console.WriteLine("远程服务器名: {0}", iphe.HostName);
                //通过主机信息获取其IP
                IPAddress[] address = iphe.AddressList;
                IPEndPoint ipep = new IPEndPoint(address[0], 80);
                //实例化一个socket用于接收网页数据
                Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //连接
                socket.Connect(ipep);
                if (socket.Connected)
                {
                    //发送头文件,这样才能下载网页数据
                    socket.Send( Encoding.ASCII.GetBytes( this.GetHeader(webPageURL)));
                }
                else { return; }
                //接收头一批数据
                var count = socket.Receive(receiveBufferBytes);
                //转化成string 
                var getString = Encoding.Default.GetString(receiveBufferBytes);
               //创建文件流
                FileStream fs = new FileStream(@"d:\\Test.html", FileMode.OpenOrCreate);
                //创建缓存流
                BufferedStream bs = new BufferedStream(fs);
                using (fs)
                {
                    using (bs)
                    {
                        byte[] finalContent = Encoding.Default.GetBytes(getString.ToCharArray());
                        //将头一批数据写入本地硬盘
                        bs.Write(finalContent, 0, finalContent.Length);
                        //循环通过socket接收数据
                        while (count > 0)
                        {
                            count = socket.Receive(receiveBufferBytes, receiveBufferBytes.Length, SocketFlags.None);
                            //直接将获取到的byte数据写入本地硬盘
                            bs.Write(receiveBufferBytes, 0, receiveBufferBytes.Length);
                            Console.WriteLine(Encoding.Default.GetString(receiveBufferBytes));
                        }
                        bs.Flush();
                        fs.Flush();
                        bs.Close();
                        fs.Close();
                    }
                }
            }
            /// <summary>
            /// 得到header
            /// </summary>
            /// <param name="url">网页url</param>
            /// <returns>header字符串</returns>
            private string GetHeader(string webPageurl) 
            {
                return "GET " + GetRelativeUrlBystrUrl(webPageurl) + " HTTP/1.1\r\nHost: "
                    + GetHostNameBystrUrl(webPageurl) + "\r\nConnection: Close\r\n\r\n";
            }
    
            /// <summary>
            /// 得到相对路径
            /// </summary>
            /// <param name="strUrl">网页url</param>
            /// <returns></returns>
            private string GetRelativeUrlBystrUrl(string strUrl)
            {
                int iIndex = strUrl.IndexOf(@"//");
                if (iIndex <= 0)
                    return "/";
                string strTemp = strUrl.Substring(iIndex + 2);
                iIndex = strTemp.IndexOf(@"/");
                if (iIndex > 0)
                    return strTemp.Substring(iIndex);
                else
                    return "/";
            }
            /// <summary>
            /// 根据Url得到host
            /// </summary>
            /// <param name="strUrl">网页url</param>
            /// <returns></returns>
            private string GetHostNameBystrUrl(string strUrl)
            {
                int iIndex = strUrl.IndexOf(@"//");
                if (iIndex <= 0)
                    return "";
                string strTemp = strUrl.Substring(iIndex + 2);
                iIndex = strTemp.IndexOf(@"/");
                if (iIndex > 0)
                    return strTemp.Substring(0, iIndex);
                else
                    return strTemp;
            }
    
        }
    复制代码

    本章总结

    本章主要讲述了BufferedStream的概念包括缓冲区等等,其中穿插了装饰器模式的简单介绍,希望大家能够BufferedStream有更深的理解,写文不容易,

    也请大家多多关注,下一章节将介绍常用的压缩流(非微软类库),谢谢大家支持!

    c#

     
    摘要: C# 温故而知新:Stream篇(六)BufferedStream目录:简单介绍一下BufferedStream如何理解缓冲区?BufferedStream的优势从BufferedStream 中学习装饰模式 如何理解装饰模式 再次理解下装饰模式在Stream中的作用BufferedStream的构造BufferedStream的属性BufferedStream的方法简单示例:利用socket 读取网页并保存在本地本章总结1 简单介绍一下BufferedStream在前几章的讲述中,我们已经能够掌握流的基本特性和特点,一般进行对流的处理时系统肩负着IO所带来的开销,调用十分频繁,这...阅读全文
    posted @ 2012-04-25 20:40 逆时针の风 阅读(53) | 评论 (0) 编辑
     
    摘要: C# 温故而知新:Stream篇(五)MemoryStream目录:1 简单介绍一下MemoryStream2 MemoryStream和FileStream的区别3 通过部分源码深入了解下MemoryStream4 分析MemorySteam最常见的OutOfMemory异常5 MemoryStream 的构造6 MemoryStream 的属性7 MemoryStream 的方法8 MemoryStream 简单示例 : XmlWriter中使用MemoryStream9 MemoryStream 简单示例 :自定义一个处理图片的HttpHandler10 本章总结简单介绍一下Memory阅读全文
    posted @ 2012-04-14 03:21 逆时针の风 阅读(1930) | 评论 (10) 编辑
     
    摘要: C# 温故而知新:Stream篇(四)FileStream目录:如何去理解FileStream?FileStream的重要性FileStream常用构造函数(重要)非托管参数SafeFileHandle简单介绍FileStream常用属性介绍FileStream常用方法介绍FileStream示例1:*文件的新建和拷贝(主要演示文件同步和异步操作)FileStream示例2:*实现文件本地分段上传本章总结如何去理解FileStream?通过前3章的学习相信大家对于Stream已经有一定的了解,但是又如何去理解FileStream呢?请看下图 我们磁盘的中任何文件都是通过2进制组成,最为直观的.阅读全文
    posted @ 2012-04-03 05:28 逆时针の风 阅读(4612) | 评论 (17) 编辑
     
    摘要: C# 温故而知新:Stream篇(三)TextWriter 和 StreamWriter目录:为何介绍TextWriter?TextWriter的构造,常用属性和方法 IFormatProvider的简单介绍如何理解StreamWriter?StreamWriter属性StreamWriter示例本章总结为何介绍TextWriter?就像上篇讲述的一样,对于重要的基础技术,我们一定要刨根问底,这样在面对将来可能很复杂的业务或技术时才能游刃有余,甚至可以创新出新的解决方案,言归正传,想了解StreamWriter 必须了解其父亲TextWriter的结构和使用方法。那么微软为什么要创建立这个抽象阅读全文
    posted @ 2012-03-25 20:14 逆时针の风 阅读(2179) | 评论 (6) 编辑
     
    摘要: C# 温故而知新:Stream篇(二)TextReader 和StreamReader目录:为什么要介绍 TextReader?TextReader的常用属性和方法TextReader 示例从StreamReader想到多态简单介绍下Encoding 编码StreamReader 的定义及作用StreamReader 类的常用方法属性StreamReader示例本章总结为什么要介绍 TextReader?首先让我们来理解下什么是TextReader,从字面上的意思入手的话,大家就会恍然大悟了一个对于Text的读取器,可是又是怎么读取的呢?聪明的你肯定会想到,当然是通过连续的字符进行读取, 为什阅读全文
    posted @ 2012-03-19 00:11 逆时针の风 阅读(2851) | 评论 (6) 编辑
     
    摘要: C# 温故而知新:Stream篇(—) 目录:什么是Stream?什么是字节序列?Stream的构造函数Stream的重要属性及方法Stream的示例Stream异步读写Stream 和其子类的类图本章总结什么是Stream?MSDN 中的解释太简洁了: 提供字节序列的一般视图(我可不想这么理解,这必定让我抓狂,我理解的流是向自然界的河流那样清澈而又美丽,c#中的流也是一样,许多技术或者说核心技术都需要流的帮忙)那什么是字节序列呢?其实简单的来理解的话字节序列指的是:字节对象都被存储为连续的字节序列,字节按照一定的顺序进行排序组成了字节序列那什么关于流的解释可以抽象为下列情况:打个比方:一条河阅读全文
    posted @ 2012-03-17 03:12 逆时针の风 阅读(3629) | 评论 (29) 编辑
     
    摘要: 最近公司从微软接了许多小项目,导致了人手不够,特别是数据库后台和前台脱节严重,部分客户已经发飙,无奈之下,昨天下午老板和我们teamLeader 之间开了个紧急会议,商量应对之道。由于前台的效率比后台高(后台大牛走的走,已经所剩无几,许多实习生或者是刚入职的),所以在下周投入后台工作之前,我向老板提出了“fakeDataTool”的理念,就是一个制造临时假数据的微型框架,能够解决燃眉之急,老板曾经是个伟大的架构师,所以他欣然接受这一个想法,于是一个微型框架的第一个版本在今天凌晨5点诞生了。。。这个版本完成的功能有:1: 制造一个类的对象,并且属性赋值 (第一本版本只有已经实现了 class ,阅读全文
    posted @ 2012-03-03 21:15 逆时针の风 阅读(632) | 评论 (9) 编辑
     
    摘要: 我们在项目经常会碰到一些特殊需求 例如下拉框是复选的,查询条件是根据下拉框中复选项进行拼接看到此图后大家肯定会说,这很简单嘛将所有的选项 拼成“'1-3','5-9'” 然后放到 in 的字句后面,一查就出来了。这样做的确在逻辑上没有问题,可是大家有没有想过这个问题,过度的和业务耦合虽然能够解决现在的需求但是却牺牲了代码优雅和可维护性其实本文的目的是想利用Expression表达式在linq查询中实现一个优雅的解决方案,同时也会给大家一个用Expression去拼接sql的思路先上代码 public static Expression<Func<T,阅读全文
    posted @ 2012-02-23 02:53 逆时针の风 阅读(578) | 评论 (9) 编辑
     
    摘要: 由于传统的反射性能不是很好,所以许多高手都想尽方法提高反射的性能以下DynamicUtilites编是其中一个(和.net4.0中的那个Dynamic反射不同)首先在项目中添加DynamicUtilites项目DynamicUtilites项目下有三个文件 Dynamic.csDynamicComparer.csDynamicEmit.cs如果由于精力有限无法了解该反射辅助类的具体实现原理,请大家原谅 ^^,大致是将原有反射代码的封装和优化,有空我会去了解下该辅助类的源码以下简单介绍如何使用该反射类先建立一个ICallable接口,注意是泛型接口s代码Code highlighting pro阅读全文
    posted @ 2010-06-14 17:58 逆时针の风 阅读(286) | 评论 (0) 编辑
     
    摘要: 代码Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/--> 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace LmbdaText 7 { 8 class Program 9 { 10 staticint[] numbers =newint[]{5,3,4,2,1,...阅读全文
    posted @ 2010-06-05 13:10 逆时针の风 阅读(248) | 评论 (0) 编辑
  • 相关阅读:
    POJ1006: 中国剩余定理的完美演绎(非原创)
    poj 1001 分析
    document.createElement()的用法
    js innertext
    转csdn-css4
    css中最基本几个选择器
    Django解决(1146, "Table 'd42.django_session' doesn't exist")方法
    django清理migration终极解决办法
    linux中的fork炸弹
    nginx转发php文件到php-fpm服务器提示502错误
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/2470473.html
Copyright © 2020-2023  润新知