C#学习心得
版本：v2.0
Crifan Li
摘要
本文主要介绍了C#中的一些学习心得，包括但不限于Regex,TreeView,字典类型变量,spritf,UrlEncode等
本文提供多种格式供：

HTML版本的在线地址为：
http://​www.crifan.com/​files/​doc/​docbook/​csharp_​summary/​release/​html/​csharp_​summary.html
有任何意见，建议，提交bug等，都欢迎去讨论组发帖讨论：
http://​www.crifan.com/​bbs/​categories/​csharp_​summary/
在线阅读	HTML  1	HTMLs  2	PDF  3	CHM  4	TXT  5	RTF  6	WEBHELP  7	
下载（7zip压缩包）	HTML  8	HTMLs  9	PDF  10	CHM  11	TXT  12	RTF  13	WEBHELP  14	
修订历史	
修订 2.0	2013-08-20	crl	
· 	将crifanLib.cs弄成独立的book
· 	整理更多的关于C#的心得
· 		
修订 1.0	2012-11-06	crl	
· 	将C#从language_summary移至此独立book
· 	更新crifanLib.cs的最新链接
· 		
C#学习心得:
Crifan Li
版本：v2.0
出版日期 2013-08-20
版权 © 2013 Crifan, http://​crifan.com

本文章遵从：署名-非商业性使用 2.5 中国大陆(CC BY-NC 2.5)15

目录
前言  	 # 

1. 本文目的  	 # 
2. 待完成  	 # 
1. C#学习心得  	 # 

1.1. C#中的字典类型变量  	 # 
1.2. C#中关于字符串的处理  	 # 

1.2.1. C#中类似于spritf的函数  	 # 
1.2.2. 字符串的中间对齐左右填充的效果  	 # 
1.3. C#中ComboBox的使用  	 # 

1.3.1. 给ComboBox设置数据源  	 # 
2. C#的网络相关处理  	 # 

2.1. C#中HTTP处理  	 # 

2.1.1. C#中自动处理返回的压缩的HTML  	 # 
2.1.2. C#中给HTTP添加代理Proxy支持  	 # 
2.1.3. ReadLine或ReadToEnd会无限期挂掉  	 # 
2.2. C#中处理cookie  	 # 

2.2.1. C#中自动处理cookie  	 # 
2.2.2. SetCookie解析有bug  	 # 
2.3. C#中处理URL  	 # 

2.3.1. 关于UrlEncode把空格编码为加号”+”而不是%20的问题  	 # 
2.4. C#中的WebBrowser  	 # 

2.4.1. DocumentCompleted被调用两次  	 # 
3. C#的处理Excel和CSV  	 # 

3.1. C#中处理Excel  	 # 

3.1.1. Excel自动适应列宽  	 # 
3.1.2. 选中Excel的某列后再去自动适应列宽  	 # 
3.1.3. Could not load file or assembly ‘Microsoft.Office.Interop.Excel  	 # 
3.1.4. Microsoft.Vbe.Interop.dll和office.dll  	 # 
3.2. C#中处理CSV  	 # 
4. C#的TreeView控件使用心得  	 # 

4.1. 添加Node的方法  	 # 
4.2. 给TreeView添加图标  	 # 
4.3. 获得右击所在节点  	 # 
4.4. 添加右键菜单（Context Menu）  	 # 
4.5. 给TreeNode添加名字  	 # 
参考书目  	 # 

前言

1. 本文目的
本文目的在于，把之前自己折腾C#的心得和注意实现都整理出来，供其他人参考。

2. 待完成
目标只整理了到了page 2：
http://www.crifan.com/category/work_and_job/programming_language/csharp/page/2/
之后的，有空再整理到此book中。

第 1 章 C#学习心得

1.1. C#中的字典类型变量
C#中也有类似于Python中的字典类型的变量
参考代码如下：

Dictionary<String, int> dicPicIdx;
Dictionary<string, int>.KeyCollection dicKeys;
 
dicPicIdx = new Dictionary<string, int>();
dicPicIdx.Add(”EmptyDocumentFolder”, 1);
dicPicIdx.Add(”NonEmptyDocumentFolder”, 1);
 
dicPicIdx.Add(”NonEmptyAlbum”, 3);
dicPicIdx.Add(”EmptyAlbum”, 3);
 
dicPicIdx.Add(”NonEmptyFavoriteFolder”, 4);
dicPicIdx.Add(”EmptyFavoriteFolder”, 4);
 
dicPicIdx.Add(”Photo”, 6);
dicPicIdx.Add(”Audio”, 7);
dicPicIdx.Add(”Video”, 8);
 
dicKeys = dicPicIdx.Keys;

1.2. C#中关于字符串的处理

1.2.1. C#中类似于spritf的函数
C#中，类似于C中常用的spritf函数，是String.Format函数。
最简单的用法举例如下：

    string spritfTestStr = String.Format(”Test sprintf in C#, number={ 0:D} , string=\ ”{ 1:s} \ ”, float={ 2:0.000} ”, 100, “crifan”, Math.PI);
    //spritfTestStr = Test sprintf in C#, number=100, string=”crifan”, float=3.142
        
关于Format函数的更多的示例，可以参考微软官方文档：String.Format Method (String, Object)1
关于其他更多不同类型的参数，比如日期，数值，枚举等，如何指定对应的格式，可以参考：
For more information about the composite formatting feature supported by methods such as Format2, AppendFormat3, and some overloads of WriteLine4, see Composite Formatting5.
For more information about numeric format specifiers, see Standard Numeric Format Strings6 and Custom Numeric Format Strings7.
For more information about date and time format specifiers, see Standard DateTime Format Strings8 and Custom DateTime Format Strings9.
For more information about enumeration format specifiers, see Enumeration Format Strings10.
For more information about formatting, see Formatting Types11 and Formatting Overview12.

1.2.2. 字符串的中间对齐左右填充的效果
代码：

//input: [4] Valid: B0009IQZFM
//output: ============================ [4] Valid: B0009IQZFM =============================
public string formatString(string strToFormat, char cPaddingChar = ‘*’, int iTotalWidth = 80)
{ 
    //auto added space
    strToFormat = “ “ + strToFormat + “ “; //” [4] Valid: B0009IQZFM “
 
    //1. padding left
    int iPaddingLen = (iTotalWidth - strToFormat.Length)/2;
    int iLefTotalLen = iPaddingLen + strToFormat.Length;
    string strLefPadded = strToFormat.PadLeft(iLefTotalLen, cPaddingChar); //”============================ [4] Valid: B0009IQZFM “
    //2. padding right
    string strFormatted = strLefPadded.PadRight(iTotalWidth, cPaddingChar); //”============================ [4] Valid: B0009IQZFM =============================”
     
    return strFormatted;
} 

详见：【已解决】C#中实现字符串的中间对齐左右填充的效果13

1.3. C#中ComboBox的使用

1.3.1. 给ComboBox设置数据源
主要包含三步：
· 	设置结构体，其中的field的名字首字母大写，支持get和set
· 	

private struct keyValueList
{ 
    public string Key{ get;set;}  // key
    public List<string> ValueStrList{ get;set;}  // the string value list for the key
} 

· 	创建对应的结构体数组
· 	

List<keyValueList> gFootprintTypeSelList; // footprint type

gFootprintTypeSelList = new List<keyValueList>();

//1. option1: Guest Blogging
keyValueList keyValueListGuestBlogging = new keyValueList();
keyValueListGuestBlogging.Key = “Guest Blogging”;
keyValueListGuestBlogging.ValueStrList = new List<string>();
//keyValueListGuestBlogging.ValueStrList.Add(”Specify Footprint”);
keyValueListGuestBlogging.ValueStrList.Add(”Guest Blogging”);
keyValueListGuestBlogging.ValueStrList.Add(”Contribute”);
keyValueListGuestBlogging.ValueStrList.Add(”Write for us”);
keyValueListGuestBlogging.ValueStrList.Add(”Guest Category”);
keyValueListGuestBlogging.ValueStrList.Add(”Submit Content”);
gFootprintTypeSelList.Add(keyValueListGuestBlogging);

                
· 	把结构体数组赋值给ComboBox的DataSource，把DisplayMember设置为对应的结构体的field的名字（全小写）
· 	

cmbFootprintType.DataSource = gFootprintTypeSelList;
cmbFootprintType.DisplayMember = “key”;

· 	详见：【已解决】C#中给ComboBox设置数据源14

第 2 章 C#的网络相关处理

2.1. C#中HTTP处理

2.1.1. C#中自动处理返回的压缩的HTML
当你处理http时，返回的html的header中包含：
Content-Encoding: deflate
或
Content-Encoding: gzip
时，说明是返回的是压缩的HTML。
想要支持压缩的html的话，C#中，在发送HttpWebRequest之前，设置AutomaticDecompression为对应的参数即可：

HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
 
......
 
req.Headers[”Accept-Encoding”] = “gzip, deflate”;
//req.AutomaticDecompression = DecompressionMethods.GZip;
req.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;

更多解释详见：
【已解决】C#中HttpWebRequest使用Proxy后异常  1

2.1.2. C#中给HTTP添加代理Proxy支持
示例代码：

using System.Net;
WebProxy gProxy = new WebProxy(”127.0.0.1”, 8087);
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.Proxy = gProxy;

详见：【已解决】C#中HttpWebRequest支持代理2

2.1.3. ReadLine或ReadToEnd会无限期挂掉
在用C#的GetResponseStream，常会遇到，ReadLine或ReadToEnd会无限期挂掉，所以，需要去加上对应的超时（timeout）设置：
详见：【已解决】C#中在GetResponseStream得到的Stream后,通过StreamReader去ReadLine或ReadToEnd会无限期挂掉 + 给StreamReader添加Timeout支持3

2.2. C#中处理cookie

2.2.1. C#中自动处理cookie
【整理】C#中如何自动处理cookie  4

2.2.2. SetCookie解析有bug
C#中，关于解析SetCookie，前后遇到多个bug，详见：
【经验记录】C#中，库函数有bug，会将http所返回的response响应中的headers头信息中的Set-Cookie值，解析错误，丢失部分cookie  5
【已解决】又发现一个C#中解析Set-Cookie的一个bug：无故地添加cookie的path域  6

2.3. C#中处理URL

2.3.1. 关于UrlEncode把空格编码为加号”+”而不是%20的问题
默认的HttpUtility.UrlEncode会把空格编码为加号，而不是很多人所期望的%20，导致很多人以为函数出了问题呢。其实，微软官方文档HttpUtility.UrlEncode方法7中，已经解释了此问题：
您可以使用 UrlEncode 方法或 UrlPathEncode 方法对 URL 编码。
但是，方法返回不同的结果。UrlEncode 方法将每个空格字符转换为加号 (+) 字符。
UrlPathEncode 方法将每个空格字符转换为字符串 “%20”，它表示一个用十六进制表示法表示的空格。
在对 URL 的路径部分编码时使用 UrlPathEncode 方法，以保证一致的已解码 URL，与执行解码的平台或浏览器无关。
所以，想要把空格编码为%20，用 UrlPathEncode 即可。

2.4. C#中的WebBrowser
C#中使用WebBrowser，相对还是很简单的，比如，打开网页，直接用uri即可：

wbsChaseFootprint.Url = new Uri(strEncodedFullFootprintUrl);

详见：【记录】C#中使用WebBrowser浏览google页面8

2.4.1. DocumentCompleted被调用两次
目前的解决办法，在DocumentCompleted中加上：

if (!e.Url.Equals(wbsChaseFootprint.Url))
{ 
    //not actually complete, do nothing
    return;
} 

详见：【已解决】C#中的WebBrowser的DocumentCompleted被调用两次9

第 3 章 C#的处理Excel和CSV

3.1. C#中处理Excel

3.1.1. Excel自动适应列宽
全选所有列再调用AutoFit：

//auto adjust column width (according to content)
Range allColumn = xlWorkSheet.Columns;
allColumn.AutoFit();

详见：【已解决】C#中操作刚导出的Excel，设置其为自动调整列宽1

3.1.2. 选中Excel的某列后再去自动适应列宽
核心逻辑：
获得对应的列的Range，再去AutoFit

Range firstColumn = xlWorkSheet.get_Range(”A1”);
firstColumn.EntireColumn.AutoFit();

详见： 【已解决】C#中操作Excel文件实现单行的自动适应列宽+C#中如何选中Excel的某列2

3.1.3. Could not load file or assembly ‘Microsoft.Office.Interop.Excel
使用C#操作Excel常会出现类似错误：
Could not load file or assembly ‘Microsoft.Office.Interop.Excel, Version=14.0.0.0
其可能原因，现总结如下：
C#的exe用到了excel的话，希望拿到别人的地方也正常运行不报错的话，
其中，别的地方：
可能没装excel
也可能装了excel，但是版本低
那么有几种选择：
· 	最好自己在exe中集成了对应的excel的dll库。
· 	关于，集成dll到exe中，简述为：
· 	把dll加到自己的资源resource中
· 	把dll加到自己的项目中，且属性设置为 嵌入的资源（Embedded Resource）
· 	自己的类的初始化函数中，加上对应的load dll的相关代码
· 	详见：【已解决】C#中集成DLL库到自己的exe程序中3
· 	即使不集成excel的dll，在引用excel的dll也要尽量引用低版本
· 	比如别人已装的excel，即Office是Office12
· 	那么你也就不要去引用Office14==Office2010：
· 	C:\ Program Files (x86)\ Microsoft Visual Studio 10.0\ Visual Studio Tools for Office\ PIA\ Office14\ Microsoft.Office.Interop.Excel.dll
· 	了，而去引用Office12==Office2007：
· 	C:\ Program Files (x86)\ Microsoft Visual Studio 10.0\ Visual Studio Tools for Office\ PIA\ Office12\ Microsoft.Office.Interop.Excel.dll
· 	或者是和对方电脑中安装的excel的版本一致
· 	当然，最好确认一下，对方的电脑中装了哪个版本的Office（的excel）
· 	比如是Office 2010==Office14，那么你也就去引用对应的Office14：
· 	C:\ Program Files (x86)\ Microsoft Visual Studio 10.0\ Visual Studio Tools for Office\ PIA\ Office14\ Microsoft.Office.Interop.Excel.dll
· 	就好了。
· 	详见：【已解决】虽然已经安装了Office的Excel但是C#的exe还是运行出错：Could not load file or assembly ‘Microsoft.Office.Interop.Excel, Version=14.0.0.04

3.1.4. Microsoft.Vbe.Interop.dll和office.dll
Microsoft.Vbe.Interop.dll和office.dll，与Microsoft.Office.Interop.Excel.dll，一起，都是属于“Microsoft Office system 的可用程序集”
所以，最好也是要在集成，Microsoft.Office.Interop.Excel.dll，时，连带的一起把：Microsoft.Vbe.Interop.dll和office.dll都集成进来。
这样，才可以避免，别人在使用exe时，内部用到excel的dll时，完整的所需要的函数，都可以自带的找到了。
不会再对于Microsoft.Vbe.Interop.dll或office.dll报错说找不到。
详见：【已解决】Microsoft.Vbe.Interop.dll和office.dll是啥5

3.2. C#中处理CSV

第 4 章 C#的TreeView控件使用心得

4.1. 添加Node的方法
当前已有一个TreeView控件trvFolder，添加节点的代码如下：

TreeNode curNode = new TreeNode(name);
trvFolder.Nodes.Add(curNode);

4.2. 给TreeView添加图标
先拖拽出来一个ImageList控件iglIcons，然后手动编辑ImageList的Images属性Collection，添加几个小图片。
对应的index分别是0,1，2，。。。，然后用：
trvFolder.ImageList = iglIcons;
将其关联到当前TreeView控件trvFolder。
最后，每次添加TreeNode的时候，多加一句：
curNode.ImageIndex = 0;
即为：

TreeNode curNode = new TreeNode(name);
curNode.ImageIndex = 0;
trvFolder.Nodes.Add(curNode);
    
其中0为图片的index，可根据自己的实际情况改为对应的index。
这样就可以实现给不同的TreeNode添加对应的图标了。

4.3. 获得右击所在节点
选择的是别的节点，对着另外一个节点右击，要获得被右击的节点，主要代码是：

private void trvCategoryTree_MouseUp(object sender, MouseEventArgs e)
{ 
if (e.Button == MouseButtons.Right)
{ 
    // Select the clicked node
    trvCategoryTree.SelectedNode = trvCategoryTree.GetNodeAt(e.X, e.Y);
} 
} 

之后通过cmsSelection_ItemClicked也就可以通过SelectedNode得到当前右击的那个TreeNode了。
详见：【已解决】如何获得C#中右击弹出菜单时对应的当前所右击那个TreeNode1

4.4. 添加右键菜单（Context Menu）
给TreeNode添加右键的总体的思路是：
· 	添加ContextMenuStrip
· 	在其下添加子菜单
· 	实现ItemClicked事件，在其中判断点击了哪个子菜单，然后添加代码做你要做的事情即可。
· 	详见：【已解决】C#中给TreeNode添加右键（Context Menu）2

4.5. 给TreeNode添加名字
TreeNode加入到TreeView但是不显示名字，是因为加入的TreeNode没有设置Text
（虽然设置了TreeNode的Name，但是实际上用于显示的是Text而不是Name）
所以，在把TreeNode加入TreeView之前，设置好对应的Text即可正常显示。
详见：【已解决】给TreeView添加TreeNode节点后但是名字没显示出来3

参考书目
[1] 正则表达式包含引号怎么办?1 
[2] c# Regex常用2 
[3] c#学习笔记《1》——regex类（个人理解）3 
[4] 为 Windows 窗体 TreeView 控件设置图标4 
[5] winform中利用Treeview模仿资源管理器实现图片文件列表5 
[6] C#键值对容器6