新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论.NET,C#,ASP,VB技术
    [返回] 中文XML论坛 - 专业的XML技术讨论区计算机技术与应用『 Dot NET,C#,ASP,VB 』 → <展现 C#> (rainbow 翻译)- 第八章 用C#写组件 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 12149 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: <展现 C#> (rainbow 翻译)- 第八章 用C#写组件 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     admin 帅哥哟,离线,有人找我吗?
      
      
      
      威望:9
      头衔:W3China站长
      等级:计算机硕士学位(管理员)
      文章:5255
      积分:18406
      门派:W3CHINA.ORG
      注册:2003/10/5

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给admin发送一个短消息 把admin加入好友 查看admin的个人资料 搜索admin在『 Dot NET,C#,ASP,VB 』的所有贴子 点击这里发送电邮给admin  访问admin的主页 引用回复这个贴子 回复这个贴子 查看admin的博客楼主
    发贴心情 <展现 C#> (rainbow 翻译)- 第八章 用C#写组件


    发信人: wuxq (很想好好休息), 信区: DotNET        
    标  题: <展现 C#> (rainbow 翻译)- 第八章 用C#写组件
    发信站: BBS 水木清华站 (Mon Apr 30 13:52:06 2001)

    第八章    用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行初始化一个字节缓冲区,它用于从返回流中读数据。暂时忽略StringBu
    ilder 类,只要返回流中仍然有要读的数据,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异常。此外,我在组件内部调用的.N
    ET框架类也可以引发异常。因为我不能在类的内部处理这些异常,所以我必须在这里处
    理它们。
        其余的代码只不过是简单的组件使用——调用标准的构造函数,存取一个属性,并
    执行一个方法。但等一下:你需要注意何时编译应用程序。一定要告诉编译器,让它引
    用你的新组件库DLL:
        csc /r:wrq.dll wrclient.cs
        现在万事俱备,你可以测试程序了。输出结果会滚屏,但你可以看到应用程序工作
    。使用了常规的表达式,你也可以增加代码,以解析返回的HTML,并依据你个人的喜好
    ,提取信息。我预想会使用到这个类新版本的SSL(安全套接字层),用于ASP+网页中的
    在线信用卡验证。
        你可能会注意到,没有特殊的using 语句用于你所创建的库。原因是你在组件的源
    文件中没有定义名字空间。
    8.2  使用名字空间工作
        你经常使用到名字空间,例如System 和System.Net。C#利用名字空间来组织程序,
    而且分层的组织使一个程序的成员传到另一个程序变得更容易。
        尽管不强制,但你总要创建名字空间,以清楚地识别应用程序的层次。.NET框架会
    给出构建这种分层的良好思想。
        以下的代码片段显示了在C#原文件中简单的名字空间 My.Test(点号表示一个分层
    等级)的声明:
    namespace My.Test
    {
      //这里的任何东西属于名字空间
    }
        当你访问名字空间中的一个成员时,也有必要使用名字空间标识符完全地验证它,
    或者利用using标志把所有的成员引入到你当前的名字空间。本书前面的例子演示了如何
    应用这些技术。
        在开始使用名字空间之前,只有少数有关存取安全的词。如果你不增加一个特定的
    存取修饰符,所有的类型将被默认为internal 。当你想从外部访问该类型时,使用 pu
    blic 。不允许其它的修饰符。
        这是关于名字空间充分的理论。让我们继续实现该理论——以下小节说明了当构建
    组件应用程序时,如何使用名字空间
        。在名字空间中包装类
        。在客户应用程序中使用名字空间
        。为名字空间增加多个类
    8.2.1  在名字空间中包装类
        既然你知道了名字空间的理论含义,那么让我们在现实生活中实现它吧。在这个和
    即将讨论到的例子中,自然选择到的名字空间是Presenting.CSharp。为了不使你厌烦,
    仅仅是把RequestWebPage包装到Presenting.CSharp中,我决定写一个类,用于 Whois查
    找(见清单8.3)。
        清单 8.3   在名字空间中实现 WhoisLookup类
    1: using System;
    2: using System.Net.Sockets;
    3: using System.IO;
    4: using System.Text;
    5:
    6: namespace Presenting.CSharp
    7: {
    8: public class WhoisLookup
    9: {
    10:  public static bool Query(string strDomain, out string strWhoisInfo)
    11:  {
    12:   const int BUFFER_SIZE = 128;
    13:
    14:   if ("" == strDomain)
    15:    throw new ArgumentException("You must specify a domain name.");
    16:
    17:   TCPClient tcpc = new TCPClient();
    18:   strWhoisInfo = "N/A";
    19:
    20:   // 企图连接 whois  服务器
    21:   if (tcpc.Connect("whois.networksolutions.com", 43) != 0)
    22:     return false;
    23:
    24:   // 获取流
    25:   Stream s = tcpc.GetStream();
    26:
    27:   // 发送请求
    28:   strDomain += "\r\n";
    29:   Byte[] bDomArr = Encoding.ASCII.GetBytes(strDomain.ToCharArray());
    30:   s.Write(bDomArr, 0, strDomain.Length);
    31:
    32:   Byte[] Buffer = new Byte[BUFFER_SIZE];
    33:   StringBuilder strWhoisResponse = new StringBuilder("");
    34:
    35:   int BytesRead = s.Read(Buffer, 0, BUFFER_SIZE);
    36:   while (BytesRead != 0 )
    37:   {
    38:   strWhoisResponse.Append(Encoding.ASCII.GetString(Buffer,0,BytesRead));

    39:   BytesRead = s.Read(Buffer, 0, BUFFER_SIZE);
    40:   }
    41:
    42:   tcpc.Close();
    43:   strWhoisInfo = strWhoisResponse.ToString();
    44:   return true;
    45:  }
    46: }
    47: }
        名字空间在第6行被声明,而且它用第7行和第47行的大括弧括住了WhoisLookup类。
    要声明自己新的名字空间,实际要做的就是这些。
        在WhoisLookup类中当然具有一些有趣代码,特别是由于它说明了使用C#进行socke
    t编程是多么的容易。在static Query method中经过 not-so-stellar域名检查之后,我
    实例化了TCPClient类型的一个对象,它用来完成具有 Whois服务器的43端口上的所有通
    讯。在第21行建立了服务器连接:
        if (tcpc.Connect("whois.networksolutions.com", 43) != 0)
        因为连接失败是预料到的结果,所以这个方法不能引发一个异常。(你还记住异常
    处理的“要”和“不要”吗?) 返回值是一个错误代码,而返回零则说明连接成功。
        对于 Whois 查找,我必须首先发出一些信息给服务器——我要查找的域名。要完成
    此项工作,首先获得一个引用给当前TCP连接的双向流(第25行)。接着附加上一个回车
    /换行对 给域名,以表示询问结束。重新以字节数组打包,向Whois 服务器发送一个请
    求(第30行)。
        余下的代码和RequestWebPage类极其相似。在该类中,我再次利用一个缓冲区从远
    程服务器读入回应。当缓冲区完成读入后,连接被断开。返回的回应被转给了调用者。
    我明确地调用 Close 方法的原因是我不想等待垃圾收集器毁坏连接。连接时间不要过长
    ,以免占用TCP端口这种稀有资源。
        在可以使用.NET 组件中的类之前,你必须把它作为一个库来编译。尽管现在有了一
    个已定义的名字空间,该编译命令仍然没有变:
        csc /r:System.Net.dll /t:library /out:whois.dll whois.cs
        注意,如果你想该库按与C#源文件相同的方法命名,就没有必要规定 /out:开关。
    规定该开关是一个良好的习惯,因为很多项目不会只由单个源文件组成。如果你规定了
    多个源文件,该库以名单中的第一个命名。
    8.2.2  在客户应用程序中使用名字空间
        由于你使用了名字空间开发组件,所以客户也要引入名字空间
        using Presenting.CSharp;
        或者给名字空间中的成员使用完全资格名(fully  qualified name),例如
        Presenting.CSharp.WhoisLookup.Query(...);
        如果你不期望在名字空间中引入的成员之间出现冲突,using  标志( directive)
    是首选,特别是由于你具有很少的类型时。使用组件的客户程序样本在清单8.4中给出。

        清单  8.4  测试 WhoisLookup     组件
    1: using System;
    2: using Presenting.CSharp;
    3:
    4: class TestWhois
    5: {
    6:  public static void Main()
    7:  {
    8:   string strResult;
    9:   bool bReturnValue;
    10:
    11:   try
    12:   {
    13:    bReturnValue = WhoisLookup.Query("microsoft.com", out strResult);
    14:   }
    15:   catch (Exception e)
    16:   {
    17:    Console.WriteLine(e);
    18:    return;
    19:   }
    20:   if (bReturnValue)
    21:    Console.WriteLine(strResult);
    22:   else
    23:    Console.WriteLine("Could not obtain information from server.");
    24:  }
    25: }
        第2行利用using 标志引入了Presenting.CSharp名字空间。现在,我无论什么时候
    引用WhoisLookup ,都可以忽略名字空间的完全资格名了。
        该程序对 microsoft.com 域进行一次Whois 查找——你也可以用自己的域名代替m
    icrosoft.com 。允许命令行参数传递域名,可使客户的用途更广。清单8.5 实现了该功
    能,但它不能实现适当的异常处理(为了使程序更短)。
        清单  8.5  传递命令行参数给Query 方法
    1: using System;
    2: using Presenting.CSharp;
    3:
    4: class WhoisShort
    5: {
    6:  public static void Main(string[] args)
    7:  {
    8:   string strResult;
    9:   bool bReturnValue;
    10:
    11:   bReturnValue = WhoisLookup.Query(args[0], out strResult);
    12:
    13:   if (bReturnValue)
    14:    Console.WriteLine(strResult);
    15:   else
    16:    Console.WriteLine("Lookup failed.");
    17:  }
    18: }
        你所必须做的就是编译这个应用程序:
        csc /r:whois.dll whoisclnt.cs
        接着可以使用命令行参数执行该应用程序。例如,以 microsoft.com参数执行
        whoisclnt microsoft.com
        当查询运行成功时,就会出现 microsoft.com的注册信息。(清单8.6 显示了输出
    的简略版本)  这是一个很方便的小程序,通过组件化的途径写成的,花不到一个小时
    。如果用C++编写,要花多长时间?很幸运,我再也想不起当第一次用C++这样做时,花
    了多长的时间。
        清单 8.6   有关 microsoft.com (简略) 的Whois 信息
    D:\CSharp\Samples\Namespace>whoisclient
    ...
    Registrant:
    Microsoft Corporation (MICROSOFT-DOM)
      1 microsoft way
      redmond, WA 98052
      US
      Domain Name: MICROSOFT.COM
      Administrative Contact:
       Microsoft Hostmaster (MH37-ORG) msnhst@MICROSOFT.COM
      Technical Contact, Zone Contact:
       MSN NOC (MN5-ORG) msnnoc@MICROSOFT.COM
      Billing Contact:
       Microsoft-Internic Billing Issues (MDB-ORG)     msnbill@MICROSOFT.COM
      Record last updated on 20-May-2000.
      Record expires on 03-May-2010.
      Record created on 02-May-1991.
      Database last updated on 9-Jun-2000 13:50:52 EDT.
      Domain servers in listed order:
      ATBD.MICROSOFT.COM      131.107.1.7
      DNS1.MICROSOFT.COM      131.107.1.240
      DNS4.CP.MSFT.NET       207.46.138.11
      DNS5.CP.MSFT.NET       207.46.138.12
    8.2.3  增加多个类到名字空间
        使WhoisLookup和RequestWebPage 类共存于同一个名字空间是多么的美妙。既然Wh
    oisLookup已是名字空间的一部分,所以你只须使RequestWebPage 类也成为该名字空间
    的一部分。
        必要的改变很容易被应用。你只需使用名字空间封装RequestWebPage 类就可以了:

    namespace Presenting.CSharp
    {
    public class RequestWebPage
    {
    ...
    }
    }
        尽管两个类包含于两个不同的文件,但在编译后,它们都是相同名字空间的一部分

        csc /r:System.Net.dll /t:library /out:presenting.csharp.dll whois.cs web
    request.cs
        你不必要按照名字空间的名字给DLL命名。然而,这样做会有助你更容易你记住,当
    编译一个客户应用程序时要引用哪一个库。
    8.3  小结
        在这一章中,你学到了如何构建一个可以在客户程序中使用的组件。最初,你不必
    关心名字空间,但后面第二个组件中介绍了该特性。名字空间在内外部均是组织应用程
    序的好办法。
        C#中的组件很容易被构建,而且只要库和应用程序共存于相同的目录,你甚至不必
    进行特殊的安装。当要创建必须被多个客户使用的类库时,步骤就有所改变——而下一
    章将会告诉你为什么。

    --

    ※ 来源:·BBS 水木清华站 smth.org·[FROM: 166.111.215.13]
    上一篇
    返回上一页
    回到目录
    回到页首
    下一篇


       收藏   分享  
    顶(0)
      




    ----------------------------------------------

    -----------------------------------------------

    第十二章第一节《用ROR创建面向资源的服务》
    第十二章第二节《用Restlet创建面向资源的服务》
    第三章《REST式服务有什么不同》
    InfoQ SOA首席编辑胡键评《RESTful Web Services中文版》
    [InfoQ文章]解答有关REST的十点疑惑

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2004/11/9 2:25:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 Dot NET,C#,ASP,VB 』的所有贴子 点击这里发送电邮给Google AdSense  访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/4/20 15:49:34

    本主题贴数1,分页: [1]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    3,410.156ms