红俊's profile蝈蝈俊的共享空间PhotosBlogListsMore Tools Help

Blog


    December 30

    sRGB和scRGB的区别

    .net FrameWork 3.0 后,我们会发现有两个Color数据结构。
    一个是:System.Drawing.Color
    一个是:System.Windows.Media.Color

    这两个结构有啥区别呢?

    下面是对这两个类的属性的一个简单比较:

      System.Drawing.Color Structure System.Windows.Media.Color Structure
    所在组件 System.Drawing.dll PresentationCore.dll
    支持的版本

    .NET Framework
    Supported in: 3.5, 3.0 SP1, 3.0, 2.0 SP1, 2.0, 1.1, 1.0

    .NET Compact Framework
    Supported in: 3.5, 2.0, 1.0

    XNA Framework
    Supported in: 1.0

    .NET Framework
    Supported in: 3.5, 3.0 SP1, 3.0

    A Gets the alpha component value of this Color structure. Gets or sets the sRGB alpha channel value of the color.
    B Gets the blue component value of this Color structure. Gets or sets the sRGB blue channel value of the color.
    G Gets the green component value of this Color structure. Gets or sets the sRGB green channel value of the color.
    R Gets the red component value of this Color structure. Gets or sets the sRGB red channel value of the color.
    ScA 不支持 Gets or sets the ScRGB alpha channel value of the color.
    ScB 不支持 Gets or sets the ScRGB blue channel value of the color.
    ScG 不支持 Gets or sets the ScRGB green channel value of the color.
    ScR 不支持 Gets or sets the ScRGB red channel value of the color.
    获得系统支持的一些颜色 在Color中定义了141种系统预定义的颜色
    调用方法如下:
    System.Drawing.Color.AliceBlue
    不在Color中定义,而是Colors中定义,获得方法类似下面写法:
    System.Windows.Media.Colors.AliceBlue
    系统一共预定义了141个颜色。
    小结 只支持 sRGB。向下兼容 同时支持 sRGB、ScRGB。不兼容3.0以下的版本

    我们可以在上面看到,关键是sRGB和ScRGB两种颜色表示方法。这两种有啥差别呢?我们来看下面三副图,先来感性的看看:

    人眼可以看到的颜色范围以及sRGB的范围

    这幅图的巧妙之外在于它通过“归一化”,用两维平面来表示三个数据。X轴是红色的比例,Y轴是绿色的比例,而Z轴是蓝色的比例,虽然Z轴没有画出来,但它的比例数据可以很方便地计算出来。比方红是0.2,绿是0.3,那么蓝就是0.5。因为它们三者加起来必须等于1,不然怎么叫“归一化”呢!图上任何一点的蓝色分量,你都可以用这个方法计算出来。

    图中的“舌形”色域空间,是人眼能够辨别的色彩空间,它的边缘围绕一道从波长从380到700(毫微米)的光谱,中间就是用红、绿、蓝三种颜色按不同比例调配出来的颜色。

    而图中的三角的区域,是 sRGB 可以表示的颜色范围。显然有一些我们人类可以看到的颜色,但是sRGB来描述的。

    scRGB的颜色范围和人眼可以看到的范围

    上面这幅图对比了 sRGB、人眼、ScRGB 可以表示的颜色范围。

    sRGB和scRGB图的区别

    上面这幅图是sRGB和ScRGB两幅图的比较,注意看放大了的云彩。

    sRGB 和 scRGB 的转换 

    在 System.Windows.Media.Color 结构中,scRGB原色其实是被储存成单精度(single-precision)的浮点数。想要容纳scRGB颜色空间,Color 结构包含四个主要的property,类型都是float,分别为ScA、ScR、ScG、ScB。
    这些property和A、R、G、B property 会相互影响,改编G property也会造成ScG property的改变,反之亦然。

    当G property 为0,ScG property 也会为0;当G property 为255,ScG property 就会为1。在这个范围之内,
    关系并非是线性的,如下表所示。

    scG G
    <= 0 0
    0.1 89
    0.2 124
    0.3 149
    0.4 170
    0.5 188
    0.6 203
    0.7 218
    0.8 231
    0.9 243
    >=1.0 255

    ScR 与 R 之间的关系,ScB与B之间的关系,以及ScG与G之间的关系,也都是一样的。ScG的值可以小于0或者大于1,以容纳超出显示器和sRGB数字范围的颜色。

    sRGB和scRGB的比较

    sRGB目标是使同一网页在不同计算机上显示时的色彩更一致,但只适用于CRT显示器。微软HD Photo项目负责人克劳说,sRGB的挑战在于它只是完整色彩空间的一个子集,当使用sRGB编码时,我们会丢掉一些色彩。

    scRGB色彩空间是sRGB扩展,对于黑色和纯绿色而言,这二者没有任何分别。二者的差别就在于scRGB能够显示人眼无法分辨的颜色,其精细程度也超过了sRGB。

    scRGB描述每个点所需要的位数是sRGB 2倍,甚至是4倍。不仅能够使用整数,还能够使用浮点数,提高图像的精细程度。

    参考资料:

    关于scRGB色彩空间
    http://hi.baidu.com/cybo/blog/item/8f24ba38bbb584c1d5622597.html

    第二章 基本的Brush画刷类 [App = Code + Markup]
    http://www.cnblogs.com/rickiedu/archive/2007/04/04/699529.html

    GDI+与WPF中的颜色简析
    http://blog.csdn.net/johnsuna/archive/2007/08/27/1761061.aspx

    简述WPF中的图像像素格式(PixelFormats)
    http://blog.csdn.net/johnsuna/archive/2007/08/28/1762901.aspx

    December 27

    SQL Server 索引基础知识(1)--- 记录数据的基本格式

    由于需要给同事培训数据库的索引知识,就收集整理了这个系列的博客。发表在这里,也是对索引知识的一个总结回顾吧。通过总结,我发现自己以前很多很模糊的概念都清晰了很多。

    不论是缓存的数据信息,还是物理保存的信息,他们的基本单位都是数据页。所以理解数据页是最最基础的知识点,本篇博客就介绍跟索引有关的数据页的一些基础知识。

    数据页的基础知识

    SQL Server 中数据存储的基本单位是页(Page)。数据库中的数据文件(.mdf 或 .ndf)分配的磁盘空间可以从逻辑上划分成页(从 0 到 n 连续编号)。磁盘 I/O 操作在页级执行。也就是说,SQL Server 每次读取或写入数据的最少数据单位是数据页。

    注意:日志文件不是用这种方式存储的,而是一系列日志记录。

    数据库被分成逻辑页面(每个页面8KB),并且在每个文件中,所有页面都被连续地从0到x编号,其中x是由文件的大小决定的。我们可以通过指定一个数据库ID、一个文件ID、一个页码来引用任何一个数据页。当我们使用ALTER DATABASE命令来扩大一个文件时,新的空间会被加到文件的末尾。也就是说,我们所扩大文件的新空间第一个数据页的页码是x+1。当我们使用DBCC SHRINKDATABASE或DBCC SHRINKFILE命令来收缩一个数据库时,将会从数据库中页码最高的页面(文件末尾)开始移除页面,并向页码较低的页面移动。这保证了一个文件中的页码总是连续的。

    在 SQL Server 中,页的大小为 8 KB。这意味着 SQL Server 数据库中每 MB 有 128 页。依次类推。根据数据库的文件大小,我们可以算出数据库有多少数据页。

    SQL Server 2005 有以下几种页类型:

    页类型 内容
    Data 当 text in row 设置为 ON 时,包含除 text、 ntext、image、nvarchar(max)、varchar(max)、varbinary(max) 和 xml 数据之外的所有数据的数据行。
    Index 索引条目。
    Text/Image 大型对象数据类型:

    text、 ntext、image、nvarchar(max)、varchar(max)、varbinary(max) 和 xml 数据。

    数据行超过 8 KB 时为可变长度数据类型列:

    varchar、nvarchar、varbinary 和 sql_variant

    Global Allocation Map、Shared Global Allocation Map 有关区是否分配的信息。
    Page Free Space 有关页分配和页的可用空间的信息。
    Index Allocation Map 有关每个分配单元中表或索引所使用的区的信息。
    Bulk Changed Map 有关每个分配单元中自最后一条 BACKUP LOG 语句之后的大容量操作所修改的区的信息。
    Differential Changed Map 有关每个分配单元中自最后一条 BACKUP DATABASE 语句之后更改的区的信息。

    数据页(Data 类型页)的结构示意图:

    每页的开头是 96 字节的标头,用于存储有关页的系统信息。此信息包括页码、页类型、页的可用空间以及拥有该页的对象的分配单元 ID。

    在数据页上,数据行紧接着标头按顺序放置。页的末尾是行偏移表,对于页中的每一行,每个行偏移表都包含一个条目。每个条目记录对应行的第一个字节与页首的距离。行偏移表中的条目的顺序与页中行的顺序相反。

    SQL Server 页的数据格式

    有关数据页的更多知识,可以通过下面这篇文章获得更详细的了解:

    估计在堆中存储数据所需的空间量
    http://technet.microsoft.com/zh-cn/library/ms189124.aspx

    另外也可以看我收集的资料:怎样查看表的数据页的结构
    http://blog.joycode.com/ghj/articles/113108.aspx

    对大型行的支持

    在 SQL Server 2005 中,行不能跨页,但是行的部分可以移出行所在的页,因此行实际可能非常大。
    (比如:一行多列时,这一行的部分列在数据页A,部分列在数据页B)
    页的单个行中的最大数据量和开销是 8,060 字节 (8 KB)。但是,这不包括用 Text/Image 页类型存储的数据。
    在 SQL Server 2005 中,包含 varcharnvarcharvarbinarysql_variant 列的表不受此限制的约束。
    当表中的所有固定列和可变列的行的总大小超过限制的 8,060 字节时,SQL Server 将从最大长度的列开始动态将一个或多个可变长度列移动到 ROW_OVERFLOW_DATA 分配单元中的页。
    每当插入或更新操作将行的总大小增大到超过限制的 8,060 字节时,将会执行此操作。
    将列移动到 ROW_OVERFLOW_DATA 分配单元中的页后,将在 IN_ROW_DATA 分配单元中的原始页上维护 24 字节的指针。
    如果后续操作减小了行的大小,SQL Server 会动态将列移回到原始数据页。

     

    SQL Server 的数据页缓存

    SQL Server 数据库的主要用途是存储和检索数据,因此密集型磁盘 I/O 是数据库引擎的一大特点。此外,完成磁盘 I/O 操作要消耗许多资源并且耗时较长,所以 SQL Server 侧重于提高 I/O 效率。缓冲区管理是实现高效 I/O 操作的关键环节。SQL Server 2005 的缓冲区管理组件由下列两种机制组成:用于访问及更新数据库页的缓冲区管理器和用于减少数据库文件 I/O 的缓冲区高速缓存(又称为“缓冲池”)。

     

    缓冲区管理的工作原理
    一个缓冲区就是一个 8KB 大小的内存页,其大小与一个数据页或索引页相当。因此,缓冲区高速缓存被划分为多个 8KB 页。缓冲区管理器负责将数据页或索引页从数据库磁盘文件读入缓冲区高速缓存中,并将修改后的页写回磁盘。页一直保留在缓冲区高速缓存中,直到已有一段时间未对其进行引用或者缓冲区管理器需要缓冲区读取更多数据。数据只有在被修改后才重新写入磁盘。在将缓冲区高速缓存中的数据写回磁盘之前,可对其进行多次修改。

    实验

    下面做一个简单的实验来看你是否已经掌握的上面的知识点:

    准备测试环境

    在一个SQL 2005数据库中,执行下面脚本。

    简单来说,就是创建了2个表,注意这两个表,一个是存储的 nchar(2019) 的字段,一个是存储的 nchar(2020) 的字段。 我们将来看这两个表在同样数据下,存储所花费的空间大小。由于缓存和物理存储的基本单位都是数据页,这个表物理存储的大小跟全部缓存的大小会是一样的。

    然后我们每个表填充20个数据。

    -- 创建2个测试表
    CREATE TABLE [dbo].[Table_2019]([Data] [nchar](2019) NOT NULL) 
    CREATE TABLE [dbo].[Table_2020]([Data] [nchar](2020) NOT NULL) 
    go
    -- 填充数据
    declare @i int
    set @i = 0
    while(@i < 20)
    begin
        insert Table_2019(Data) values('')
        insert Table_2020(Data) values('')
        select @i = @i + 1
    end
    go 
    这里我们用 nchar 数据类型,是因为:
    当指定了 NOT NULL 子句时,nchar 数据类型是一种长度固定的数据类型。
    如果插入值的长度比 nchar NOT NULL 列的长度小,将在值的右边填补空格直到达到列的长度。
    例如,如果某列定义为 nchar(10),而要存储的数据是“music”,则 SQL Server 将数据存储为“music_____”,这里“_”表示空格。
    http://technet.microsoft.com/zh-cn/library/ms175055.aspx
    这样我们填充测试数据的脚本就非常简单。
    而且计算数据行所占的空间也非常简单。
    另外,我们建立的这两个表都没有索引,所以他们都是堆,有关估计在堆中存储数据所需的空间量请参看以下文章:
    http://technet.microsoft.com/zh-cn/library/ms189124.aspx
    完成准备工作后,我们来查看这两个所占空间的大小。在 SQL Server Management Studio 中,我们选择测试数据库, 然后在右键菜单中依次选择 
    Reports --> Standard Reports --> Disk Usage by Top Tables 或者Disk Usage by Table 就可以看到下面统计数据。
                 
    Disk Usage by Top Tables: [ghj_Demo]  
    on GHJ1976-PC\SQLEXPRESS at 2007/12/27 9:21:33  
       
                 
    This report provides detailed data on the utilization of disk space by top 1000 tables within the Database.  
                 
    Table Name # Records Reserved (KB) Data (KB) Indexes (KB) Unused (KB)
    dbo.Table_2020 20 200 160 8 32
    dbo.Table_2019 20 136 80 8 48

     

    这两个表同样20条记录。Table_2020 表数据占了 160kb ,即 20 个数据页。Table_2019 表数据占了 80 kb,即 10 个数据页。
    为何会这样呢?
    Table_2020  表的1个数据页只能放下1个数据行。
    Table_2019  表的1个数据页只能放下2个数据行。
    这两个表的字段长度只差2个字节,但是物理存储却是一倍的差距。

     

    参考资料:

    SQL Server数据库中存储引擎深入探讨
    http://tech.ccidnet.com/art/1106/20070320/1040665_3.html

    《Microsoft SQL Server 2005技术内幕:存储引擎》 这本书电子版的一部分
    http://book.csdn.net/bookfiles/504/10050417350.shtml

    MSDN 中关于“页和区”的描述
    http://technet.microsoft.com/zh-cn/library/ms190969.aspx 

    聚集索引结构
    http://technet.microsoft.com/zh-cn/library/ms177443.aspx

    行溢出数据超过 8 KB
    http://technet.microsoft.com/zh-cn/library/ms186981.aspx

    缓冲区管理
    http://technet.microsoft.com/zh-cn/library/aa337525.aspx

    估计堆的大小
    http://technet.microsoft.com/zh-cn/library/ms189124.aspx

    nchar 和 nvarchar (Transact-SQL)
    http://technet.microsoft.com/zh-cn/library/ms186939.aspx

    Teched 2007 上 吴家震 主讲的"微软SQL服务器Always-On Tech-nologies: 高级索引策略"  录像下载地址:
    http://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032364059&Culture=zh-CN
    注意, 这个页面标示的是 "SharePoint 2007 网站性能调优" ,但是其实是高级索引策略,微软弄错文件了,害得我一个个下下来看,哪个是需要的录像.

    December 20

    VS2008使用VSS做源代码管理需要注意的一点

    上个月 Scott Guthrie 的博客中提到, VS2008 如果用 VSS 做源代码管理,会有一些bug。 在他的博客中提到:

    “我们正在更新Visual SourceSafe 2005,以使它能和VS 2008合作。我们原先计划在上个星期就发布的,但在发布前发现了一个缺陷,会延迟几个星期。我们目前计划在几个星期内发布。Brian Harry在这里的博客帖子里对此有详述。”

    相关地址:http://blog.joycode.com/scottgu/archive/2007/11/27/111998.aspx

    我这些天都在等这个bug,我在用VS2008+VSS2005中,总碰到一些怪异的小问题。也没看到国内有人讨论这个问题。一直以为这个补丁没发布,今天自己去查了一下,原来这个补丁11号的时候就发布了。自己现在才知道。

    至于有些人想象中的VSS2008则不会出现。VS2008下如果用VSS做源代码管理的话,应该是打了这个补丁的VSS2005。

     

    VS2005时带的VSS2005版本号是: version 8.0.50727.42

    打了这个补丁后得版本号是:version 8.0.50727.1551(VS2008 用)

    这个补丁下载地址:

    Download the VS80-KB943847-X86-INTL.exe package now.

    这个补丁包有3.14M

     

    这个补丁修复了那些bug,请到 http://support.microsoft.com/kb/943847 去看。

     

     

    参考资料:

    VS 2008 and SourceSafe Q&A

    http://blogs.msdn.com/richardb/archive/2007/12/03/vs-2008-and-sourcesafe-q-a.aspx

    VSS2005 的官方网址

    http://msdn2.microsoft.com/en-us/vstudio/aa718670.aspx

    December 07

    使用 Request.QueryString 接受参数时,跟编码有关的一些问题

    我们先来看以下几个请求,看a.aspx 页面用Request.QueryString接受到的是啥信息?

      页面URL Request.QueryString["info"]接受到的值
    案例一 a.aspx?info=%25

    %

    案例二 a.aspx?info=%bc%bc%ca%f5

    ����

    情况分析:

    案例一

    a.aspx?info=%25 为何 Request.QueryString["info"]接受到的值是 % ,而不是 %25,是因为Request.QueryString 替我们在接受到值后,做了一次URL解码。 HttpUtility.UrlDecode("%25")  的计算结果就是 %

    上面的这个案例一虽然看起来很简单。但是我们在一些特殊场景时候,就会因为这个而极度郁闷。

    比如以下几种情况:

    你有一个自己的加密算法,而这个加密算法,某些情况下会计算出带百分号的结果,而这个结果你是要通过URL参数的方式传递给其它页面的。
    这时候你就苦恼的发现,某些时候某个功能就不能用。

    如果解决案例一碰到的情况呢?

    解决方案一:

    把需要传递的参数传递前作一次 HttpUtility.UrlEncode ,
    记得是按照 UTF-8 的编码的 UrlEncode 。这样如果我们希望客户端接受到的是 %25  就应该传递的是 %2525 。

    切记,不可在接受方每次接受后,自作聪明的都做一次 UrlEncode 。而是在发送方做 UrlEncode 。
    如果接受方接受后作 UrlEncode 的话,就会出现下面情况:
    发送方发送 a.aspx?info=%25 ,这时候如果接受方  接受后作 UrlEncode 的话,一切正确
    发送方发送 a.aspx?info=% ,这时候如果接受方  接受后作 UrlEncode 的话,则就乱了。

    另:这套方案中切记, UrlEncode  和 UrlDecode 的次数应该一一对应。不能多一次,也不能少一次。
    有人就会说,这还会出现次数不对应么? 比如下面情况,一个不留意就很可能出现次数不对应。而出现不是你所期望的情况。
    比如我们有这样类似的功能:

    a.aspx 页面中,根据传入的 from 参数,自动跳转到 from 参数(用Request.QueryString["from"]来接受这个参数)设置的页面。
    b.aspx 页面也是同样的逻辑,根据传入的 from 参数(用Request.QueryString["from"]来接受这个参数),自动跳转到指定的页面。
    c.aspx 页面也是同样的逻辑,根据传入的 from 参数(用Request.QueryString["from"]来接受这个参数),自动跳转到指定的页面。


    这样我们就可能书写下面的链接地址:
    a.aspx?from=b.aspx 
    a.aspx?from=b.aspx?from=c.aspx
    a.aspx?from=b.aspx?from=c.aspx?from=http://blog.joycode.com/ghj/

    下面再复杂一点,我给下面几个链接,其中都有 a 这个参数,请告诉我 a 这个参数是被那个页面接受到了?
    说明:  HttpUtility.UrlEncode("&")  == "%26"     HttpUtility.UrlEncode("%")  == "%25"

    地址 a 参数会被那个页面接受到
    a.aspx?from=b.aspx?from=c.aspx&a=1 a 参数被 a.aspx 页面接受到了
    a.aspx?from=b.aspx?from=c.aspx%26a=1 a 参数被 b.aspx 页面接受到了
    a.aspx?from=b.aspx?from=c.aspx%2526a=1 a 参数被 c.aspx 页面接受到了

    如果想不明白,就想想下面这句话
    每一次用 Request.QueryString 获取参数时候,就作了一次 HttpUtility.UrlDecode。

    解决方案二:

    不用 Request.QueryString ,而是自己实现一个获取查询参数的方法。细节我在案例二讲完后再告诉大家,因为这个解决方案也处理了案例二的一些情况。

    案例二

    a.aspx?info=%bc%bc%ca%f5 传给我们的信息其实是使用 GB2312 编码后的“技术” 这两个汉字。
    不信,你可以用下面表达式计算的结果就是 %bc%bc%ca%f5
    HttpUtility.UrlEncode("技术", System.Text.Encoding.GetEncoding("GB2312"))

    ASP.net 系统内部,在处理 Request.QueryString 等情况时候,都是使用的 UTF-8 的编码,我们如果不存在多系统并存的问题时候,这个问题一点都不存在。
    但是,当需要跟其它系统交互式后,问题就可能会出现。
    如果你不了解案例二这里情况时,你就会被这个问题苦恼死。

    比如下面这两个地址提到的问题:

    ASP.net中的Server.UrlEncode函数和ASP中的Server.URLEncode函数返回的值竟然不一样
    http://blog.joycode.com/ghj/archive/2003/10/20/2992.aspx

    PHP与aspx之间中文通过URL如何传递?
    http://topic.csdn.net/u/20071018/19/8a4066af-a08c-4214-91e9-ed4caf977e07.html

    案例二的解决方案
    使用带编码的 HttpUtility.ParseQueryString 函数

    就是采用类似下面代码的方式,来获得指定格式编码的查询文本参数。

               System.Collections.Specialized.NameValueCollection nv =
    System.Web.HttpUtility.ParseQueryString(Request.Url.Query, System.Text.Encoding.GetEncoding("GB2312"));
               Response.Write(nv["Tag"]);

     

    要说我为啥知道上面几种解决方案,是因为我用 Reflector 看了 Request.QueryString 的实现代码。在查看代码时候,我们会看到这样一个 internal 方法:
    System.Web.HttpValueCollection 类的内部方法:
    internal void FillFromString(string s, bool urlencoded, Encoding encoding)

    这个内部方法实现了,按需解密查询参数的功能,但是遗憾的是,在QueryString 的处理函数中,强制指定了解析 QueryString 时,必须作一次 HttpUtility.UrlDecode。参看如下代码:

    public static NameValueCollection ParseQueryString(string query, Encoding encoding)
    {
        ...
        return new HttpValueCollection(query, false, true, encoding);
    }

    如果我们不想采用案例一的解决方案一,我们就需要自己写一个解析查询信息的代码。我们完全可以照抄 System.Web.HttpValueCollection 类的 internal void FillFromString(string s, bool urlencoded, Encoding encoding) 方法来改写。但郁闷的是:如果你用 Reflector 察看这个函数的实现时候,Reflector 出来的代码是错误的。正确的方法如下:是在施凡帮助下完成的。

    自己实现从 URL 查询文本 Query 中解析出我们自己需要的文本的方法

    /// <summary>
    /// 根据 URL 中的 查询文本 Query 解析成一个 NameValueCollection
    /// 在装配脑袋帮助下 郭红俊 改编自 System.Web.HttpValueCollection 类的内部方法:
    /// internal void FillFromString(string s, bool urlencoded, Encoding encoding)
    /// </summary>
    /// <param name="query">需要解析的查询文本</param>
    /// <param name="urlencoded">解析文本时候是否需要URL解码</param>
    /// <param name="encoding">解析文本时候,按照那种URL编码进行解码</param>
    /// <returns></returns>
    public static NameValueCollection FillFromString(string query, bool urlencoded, Encoding encoding)
    {
        NameValueCollection queryString = new NameValueCollection();
        if (string.IsNullOrEmpty(query))
        {
            return queryString;
        }

        // 确保 查询文本首字符不是 ?
        if (query.StartsWith("?"))
        {
            query = query.Substring(1, query.Length - 1);
        }

        int num1 = (query != null) ? query.Length : 0;
        // 遍历每个字符
        for (int num2 = 0; num2 < num1; num2++)
        {
            int num3 = num2;
            int num4 = -1;
            while (num2 < num1)
            {
                switch (query[num2])
                {
                    case '=':
                        if (num4 < 0)
                        {
                            num4 = num2;
                        }
                        break;
                    case '&':
                        goto BREAKWHILE;
                }
                num2++;
            }

        BREAKWHILE:

            string name = null;
            string val = null;
            if (num4 >= 0)
            {
                name = query.Substring(num3, num4 - num3);
                val = query.Substring(num4 + 1, (num2 - num4) - 1);
            }
            else
            {
                val = query.Substring(num3, num2 - num3);
            }
            if (urlencoded)
            {

                queryString.Add(HttpUtility.UrlDecode(name, encoding), HttpUtility.UrlDecode(val, encoding));
            }
            else
            {
                queryString.Add(name, val);
            }
            if ((num2 == (num1 - 1)) && (query[num2] == '&'))
            {
                queryString.Add(null, string.Empty);
            }
        }

        return queryString;

    }

    用上面的代码,我们就可以按需解析自己需要的查询参数,而不是受限的使用Request.QueryString 。

    小结

          Request.QueryString 替我们件事情:每次接受到参数后,都做 UrlEncode ,并且是按照 UTF-8编码做的 UrlEncode 。 这在大多数情况下没有任何问题,但是一些情况下,会给我们带来麻烦,本文就是分析这些可能给我们带来麻烦的场景,以及解决方法。

    参考资料:

    使用 Reflector ; 查看代码时候,碰到的一个Reflector 的bug
    http://blog.joycode.com/ghj/archive/2006/12/06/88646.aspx

    解密不同编码的的参数。
    http://blog.joycode.com/ghj/archive/2006/04/19/74894.aspx

    两个Cookie类

    .Net 提供了两个Cookie类:

    System.Web.HttpCookie  类 和 System.Net.Cookie 类

    对应的有两个Cookie 集合类

    System.Web.HttpCookieCollection 类 和 System.Net.CookieCollection 类

    我们一般来理解他们的区别就是下面简单的一句:

    System.Web 命名空间下的是给服务器段用的,System.Net 是给客户端程序用的。

    实际上不止这点区别:

     

    下面我们来对比这两个Cookie类的属性如下,这些属性都是Copy自MSDN中文版的说明文档:

    System.Web.HttpCookie  类 System.Net.Cookie 类
    MSDN中对构造函数的描述:
    已重载。 初始化 HttpCookie 类的新实例。
    MSDN中对构造函数的描述:
    已重载。 根据 Netscape 规范初始化 Cookie 类的新实例。通常,应用程序无需构造 Cookie 类,因为该类会基于通过 HTTP 响应接收的 Set-Cookie 标头自动创建。
     

    Comment 

    获取或设置服务器可添加到 Cookie 中的注释。

     

    CommentUri 

    获取或设置服务器可通过 Cookie 来提供的 URI 注释。

     

    Discard 

    获取或设置由服务器设置的丢弃标志。

    Domain
    获取或设置将此 Cookie 与其关联的域。

    Domain 
    获取或设置 Cookie 对其有效的 URI。

     

    Expired 

    获取或设置 Cookie 的当前状态。

    Expires 
    获取或设置此 Cookie 的过期日期和时间。

    Expires 
    获取或设置作为 DateTime 的 Cookie 过期日期和时间。

     

    HttpOnly 
    确定页脚本或其他活动内容是否可访问此 Cookie。

    Name 
    获取或设置 Cookie 的名称。

    Name 
    获取或设置 Cookie 的名称。

    Path 
    获取或设置要与当前 Cookie 一起传输的虚拟路径。

    Path 
    获取或设置此 Cookie 适用于的 URI。

     

    Port 
    获取或设置此 Cookie 适用于的 TCP 端口的列表。

    Secure 
    获取或设置一个值,该值指示是否使用安全套接字层 (SSL)(即仅通过 HTTPS)传输 Cookie。

    Secure 
    获取或设置 Cookie 的安全级别。

     

    TimeStamp 
    获取此 Cookie 作为 DateTime 发出的时间。

    Value 
    获取或设置单个 Cookie 值。

    Value 
    获取或设置 Cookie 的 Value。

    Values 
    获取单个 Cookie 对象所包含的键值对的集合。

     

     

    Version 
    获取或设置此 Cookie 符合的 HTTP 状态维护版本。

    你会看到 System.Net.Cookie 类 比 System.Web.HttpCookie  类多好些属性,一些我们WEB开发人员都不清楚的属性。为什么呢?

    这就要从 cookie规范 说起。目前有以下几种Cookie规范:

    • Netscape cookie草案:是最早的cookie规范,基于rfc2109。尽管这个规范与rc2109有较大的差别,但是很多服务器都与之兼容。
    • rfc2109, 是w3c发布的第一个官方cookie规范。理论上讲,所有的服务器在处理cookie(版本1)时,都要遵循此规范。遗憾的是,这个规范太严格了,以致很多服务器不正确的实施了该规范或仍在使用Netscape规范。
    • rfc2965规范定义了cookie版本2,并说明了cookie版本1的不足。

    rfc2965规范的使用,目前并不多。rfc2109规范相应要严格得多,在实际应用上,并不是所有的浏览器和Web服务器都严格遵守。因此相比较而言,Netscape cookie草案倒是一个比较简洁和被广泛支持的Cookie规范。

     

    回过来我们再看 System.Web.HttpCookie 类  和 System.Net.Cookie 类的区别

    我理解的他们的区别应该是:

     

    System.Web.HttpCookie 类

    这个类最初设计是考虑是WEB服务器用的,由于微软的WEB服务器并没有遵循 rfc2109 \rfc2965 规范。而是采用的 Netscape cookie草案方案。

    同时为了兼顾以前ASP的一些编码习惯,于是就有了这个类这样的设计。

     

    在 dudu 之前的一篇博客中提到的 

    遍历System.Web.HttpCookieCollection, 会有如下的写法:

    foreach (string name in Request.Cookies)
    {
        info += string.Format("{0} = {1} \r\n <br />", name, Request.Cookies[name].Value);
    }

    而 foreach(HttpCookie cookie in Request.Cookies)会出错。 为何微软会有这样的设计就可以理解了。

     

     

    System.Net.Cookie 类

    这个类最初设计时候应该是考虑主要是客户端使用的,

    由于考虑到有些服务器的Cookie 是遵循 rfc2109 \rfc2965 规范,所以这个类的设计多了那些属性。

     

     

    相关资料:

     

    System.Net.Cookie和System.Web.HttpCookie有什么区别

    http://topic.csdn.net/t/20050304/15/3824900.html

     

    为什么foreach(HttpCookie cookie in Request.Cookies)会出错

    http://www.cnblogs.com/dudu/archive/2004/12/21/80118.html

     

    HTTP代理如何正确处理Cookie

    http://www.ibm.com/developerworks/cn/java/j-cookie/

     

    Netscape cookies 草案

    http://wp.netscape.com/eng/mozilla/3.0/handbook/javascript/cookies.htm

     

    W3C的 rfc2109 规范

    http://www.w3.org/Protocols/rfc2109/rfc2109.txt

     

    W3C的 rfc2965 规范

    http://www.ietf.org/rfc/rfc2965.txt