【 tulaoshi.com - 编程语言 】
第八章 用C#写组件
这一章关于用C#写组件。你学到如何写一个组件,如何编译它,且如何在一个客户程序中使用它。更深入一步是运用名字空间来组织你的应用程序。
这章由两个主要大节构成:
。你的第一个组件
。使用名字空间工作
8.1 你的第一个组件
到目前为止,在本书中提到的例子都是在同一个应用程序中直接使用一个类。类和它的使用者被包含在同一个执行文件中。现在我们将把类和使用者分离到组件和客户,它们分别位于不同的二进制文件中(可执行文件)。
尽管你仍然为组件创建一个 DLL,但其步骤与用C++写一个COM组件差别很大。你很少涉及到底层结构。以下小节说明了如何构建一个组件以及使用到它的客户:
。构建组件
。编译组件
。创建一个简单的客户应用程序
8.1.1 构建组件
因为我是一个使用范例迷,我决定创建一个相关Web的类,以方便你们使用。它返回一个Web网页并储存在一个字符串
变量中,以供后来重用。所有这些编写都参考了.NET框架的帮助文档。
类名为RequestWebPage;它有两个构造函数—— 一个属性和一个方法。属性被命名为URL,且它储存了网页的Web地
址,由方法GetContent返回。这个方法为你做了所有的工作(见清单8.1)。
清单 8.1 用于从Web服务器返回HTML网页的RequestWebPage 类
1: using System;
2: using System.Net;
3: using System.IO;
4: using System.Text;
5:
6: public class RequestWebPage
7: {
8: private const int BUFFER_SIZE = 128;
9: private string m_strURL;
10:
11: public RequestWebPage()
12: {
13: }
14:
15: public RequestWebPage(string strURL)
16: {
17: m_strURL = strURL;
18: }
19:
20: public string URL
21: {
22: get { return m_strURL; }
23: set { m_strURL = value; }
24: }
25: public void GetContent(out string strContent)
26: {
27: // 检查 URL
28: if (m_strURL == "")
29: throw new ArgumentException("URL must be provided.");
30:
31: WebRequest theRequest = (WebRequest) WebRequestFactory.Create(m_strURL);
32: WebResponse theResponse = theRequest.GetResponse();
33:
34: // 给回应设置字节缓冲区
35: int BytesRead = 0;
36: Byte[] Buffer = new Byte[BUFFER_SIZE];
37:
38: Stream ResponseStream = theResponse.GetResponseStream();
39: BytesRead = ResponseStream.Read(Buffer, 0, BUFFER_SIZE);
40:
41: //使用 StringBuilder 以加速分配过程
42: StringBuilder strResponse = new StringBuilder("");
43: while (BytesRead != 0 )
44: {
45: strResponse.Append(Encoding.ASCII.GetString(Buffer,0,BytesRead));
46: BytesRead = ResponseStream.Read(Buffer, 0, BUFFER_SIZE);
47: }
48:
49: // 赋给输出参数
50: strContent = strResponse.ToString();
51: }
52: }
本应该利用无参数构造函数完成工作,但我决定在构造函数中初始化URL,这可能会很有用。当后来决定要改变URL
时——为了返回第二个网页,例如,通过URL属性的get和set访问标志使它被公开了。
有趣的事始于GetContent方法。首先,代码对URL实行十分简单的检查,如果它不适合,就会引发一个
ArgumentException 异常。之后,我请求WebRequestFactory ,以创建一个基于传递给它的URL的WebRequest对象。
因为我不想发送cookies、附加头和询问串等,所以立即访问WebResponse(第32行)。如果你需要请求上述任何的功
能,必须在这一行之前实现它们。
第35和36行初始化一个字节缓冲区,它用于从返回流中读数据。暂时忽略StringBuilder 类,只要返回流中仍然有要
读的数据,while循环就会简单地重复。最后的读操作将返回零,因此结束了该循环。
现在我想回到StringBuilder类。为什么用这个类的实例而不是简单地把字节缓冲区合并到一个字符串变量?看下面这
个例子:
strMyString = strMyString + "some more text";
这里很清楚,你正在拷贝值。常量 "some more text" 以一个字符串变量类型被加框,且根据加法操作创建了一个新
的字符串变量。接着被赋给了 strMyString。有很多次拷贝,是吗?
但你可能引起争论
strMyString += "some more text";
不要炫耀这种行为。对不起,对于C#这是一个错误的答案。其操作完全与所描述的赋值操作相同。
不涉及该问题的另外的途径是使用StringBuilder类。它利用一个缓冲区进行工作,接着,在没有发生我所描述的拷贝
行为的情况下,你进行追加、插入、删除和替换操作。这就是为什么我在类中使用它来合并那些读自缓冲区中的内容。
该缓冲区把我带进了这个类中最后重要的代码片段——第45行的编码转换。它只不过涉及到我获得请求的字符集。
最后,当所有的内容被读入且被转换时,我显式地从 StringBuilder请求一个字符串对象并把它赋给了输出变量。一
个返回值仍然会导致另外的拷贝操作。
8.1.2 编译组件
到目前为止,你所做的工作与在正常应用程序的内部编写一个类没有什么区别。所不同的是编译过程。你必须创建一
个库而不是一个应用程序:
csc /r:System.Net.dll /t:library /out:wrq.dll webrequest.cs
编译开关/t:library 告诉C#编译,要创建一个库而不是搜寻一个静态 Main方法。同样,因为我正在使用
System.Net名字空间,所以必须引用 (/r:)它的库,这个库就是System.Net.dll。
你的库命名为 wrq.dll,现在它准备用于一个客户应用程序。因为在这章中我仅使用私有组件工作,所以你不必把库
拷贝到一个特殊的位置,而是拷贝到客户应用程序目录。
8.1.3 创建一个简单的客户应用程序
当一个组件被写成且被成功地编译时,你所要做的就是在客户应用程序中使用它。我再次创建了一个简单的命令行应
用程序,它返回了我维护的一个开发站点的首页(见清单8.2)。
清单 8.2 用 RequestWebPage 类返回一个简单的网页
1: using System;
2:
3: class TestWebReq
4: {
5: public static void Main()
6: {
7: RequestWebPage wrq = new RequestWebPage();
8: wrq.URL = "http://www.alphasierrapapa.com/iisdev/";
9:
10: string strResult;
11: try
12: {
13: wrq.GetContent(out strResult);
14: }
15: catch (Exception e)
16: {
17: Console.WriteLine(e);
18: return;
19: }
20:
21: Console.WriteLine(strResult);
22: }
成员
注意,我已经在一个try catch语句中包含了对 GetContent的调用。其中的一个原因是GetContent可能引发一个
ArgumentException异常。此外,我在组件内部调用的.NET框架类也可以引发异常。因为我不能在类的内部处理这些异常,
所以我必须在这里处理它们。
其余的代码只不过是简单的组件使用——调用标准的构造函数,存取一个属性,并执行一个方法。但等一下:你需要
注意何时编译应用程序。一定要告诉编译器,让它引用你的新组件库DLL:
csc /r:wrq.dll wrclient.cs
现在万事俱备,你可以测试程序了。输出结果会滚屏,但你可以看到应用程序工作。使用了常规的表达式,你也可以
增加代码,以解析返回的HTML,并依据你个人的喜好,提取信息。我预想会使用到这个类新版本的SSL(安全套接字层),
用于ASP+网