100165819.htm

来自「C#高级编程(第三版),顶死你们。。 。up」· HTM 代码 · 共 264 行 · 第 1/4 页

HTM
264
字号
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp; }</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp; // rest of class</span></p>
<p class="2" style="MARGIN: 0cm 0cm 8.15pt 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">}</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">在这个例子中,如果</span><span lang="EN-US">SomeClass</span><span style="FONT-FAMILY: 宋体">派生于</span><span lang="EN-US">IDisposable</span><span style="FONT-FAMILY: 宋体">,但不包含与</span><span lang="EN-US">IDisposable</span><span style="FONT-FAMILY: 宋体">中签名相同的</span><span lang="EN-US">Dispose()</span><span style="FONT-FAMILY: 宋体">实现,就会得到一个编译错误,因为该类破坏了实现</span><span lang="EN-US">IDisposable</span><span style="FONT-FAMILY: 宋体">的契约。当然,编译器允许类有一个不派生于</span><span lang="EN-US">IDisposable</span><span style="FONT-FAMILY: 宋体">的</span><span lang="EN-US">Dispose()</span><span style="FONT-FAMILY: 宋体">方法。问题是其他代码无法识别出</span><span lang="EN-US">SomeClass</span><span style="FONT-FAMILY: 宋体">支持</span><span lang="EN-US">IDisposable</span><span style="FONT-FAMILY: 宋体">特性。</span></p>
<p class="a3" style="MARGIN-TOP: 8.15pt; TEXT-INDENT: 21.45pt"><span style="FONT-FAMILY: 黑体">注意:</span></p>
<p class="a1" style="MARGIN-BOTTOM: 8.15pt; TEXT-INDENT: 21.45pt"><span lang="EN-US">IDisposable</span><span style="FONT-FAMILY: 楷体_GB2312">是一个相当简单的接口,它只定义了一个方法。大多数接口都包含许多成员。</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">接口的另一个例子是</span><span lang="EN-US">C#</span><span style="FONT-FAMILY: 宋体">中的</span><span lang="EN-US">foreach</span><span style="FONT-FAMILY: 宋体">循环。实际上,</span><span lang="EN-US">foreach</span><span style="FONT-FAMILY: 宋体">循环的内部工作方式是查询对象,看看它是否实现</span><span lang="EN-US">System.Collections.IEnumerable</span><span style="FONT-FAMILY: 宋体">接口。如果是,</span><span lang="EN-US">C#</span><span style="FONT-FAMILY: 宋体">编译器就插入</span><span lang="EN-US">IL</span><span style="FONT-FAMILY: 宋体">代码,使用这个接口上的方法迭代集合中的成员,否则,</span><span lang="EN-US">foreach</span><span style="FONT-FAMILY: 宋体">就会引发一个异常。第</span><span lang="EN-US">9</span><span style="FONT-FAMILY: 宋体">章将详细介绍</span><span lang="EN-US">IEnumerable</span><span style="FONT-FAMILY: 宋体">接口。但应注意,</span><span lang="EN-US">IEnumerable</span><span style="FONT-FAMILY: 宋体">和</span><span lang="EN-US">IDisposable</span><span style="FONT-FAMILY: 宋体">在某种程度上都是有点特殊的接口,因为它们都可以由</span><span lang="EN-US">C#</span><span style="FONT-FAMILY: 宋体">编译器识别,在</span><span lang="EN-US">C#</span><span style="FONT-FAMILY: 宋体">编译器生成的代码中会考虑它们。显然,自己定义的接口就没有这个特权。</span></p>
<h3 style="MARGIN: 8.15pt 0cm"><span lang="EN-US">4.4.1&nbsp; </span><span style="FONT-FAMILY: 黑体">定义和实现接口</span></h3>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">下面开发一个遵循接口继承规范的小例子来说明如何定义和使用接口。这个例子建立在银行账户的基础上。假定编写代码,最终允许在银行账户之间进行计算机转账业务。许多公司可以实现银行账户,但它们都是彼此赞同表示银行账户的所有类都实现接口</span><span lang="EN-US">IBankAccount</span><span style="FONT-FAMILY: 宋体">。该接口包含一个用于存取款的方法和一个返回余额的属性。这个接口还允许外部代码识别由不同银行账户执行的各种银行账户类。我们的目的是允许银行账户彼此通信,以便在账户之间进行转账业务,但还没有介绍这个功能。</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">为了使例子简单一些,我们把例子的所有代码都放在同一个源文件中,但实际上不同的银行账户类会编译到不同的程序集中,而这些程序集位于不同银行所属的不同机器上。第</span><span lang="EN-US">16</span><span style="FONT-FAMILY: 宋体">章在讨论远程通信时,将介绍位于不同机器上的</span><span lang="EN-US">.NET</span><span style="FONT-FAMILY: 宋体">程序集如何通信。但那些内容对于这里的例子来说过于复杂了。但是为了保留一定的真实性,我们为不同的公司定义不同的命名空间。</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">首先,需要定义</span><span lang="EN-US">IBank</span><span style="FONT-FAMILY: 宋体">接口:</span></p>
<p class="2" style="MARGIN: 8.15pt 0cm 0pt 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">namespace Wrox.ProCSharp</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">{</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp; public interface IBankAccount</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp; {</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void PayIn(decimal amount);</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bool Withdraw(decimal amount);</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; decimal Balance</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get;</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp; }</span></p>
<p class="2" style="MARGIN: 0cm 0cm 8.15pt 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">}</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体; LETTER-SPACING: -0.1pt">注意,接口的名称为</span><span lang="EN-US" style="LETTER-SPACING: -0.1pt">IBankAccount</span><span style="FONT-FAMILY: 宋体; LETTER-SPACING: -0.1pt">。接口名称传统上以字母</span><span lang="EN-US" style="LETTER-SPACING: -0.1pt">I</span><span style="FONT-FAMILY: 宋体; LETTER-SPACING: -0.1pt">开头,以便知道这是一个接口。</span></p>
<p class="a3" style="MARGIN-TOP: 8.15pt; TEXT-INDENT: 21.45pt"><span style="FONT-FAMILY: 黑体">注意:</span></p>
<p class="a1" style="MARGIN-BOTTOM: 8.15pt; TEXT-INDENT: 21.45pt"><span style="FONT-FAMILY: 楷体_GB2312">如第</span><span lang="EN-US">2</span><span style="FONT-FAMILY: 楷体_GB2312">章所述,在大多数情况下,</span><span lang="EN-US">.NET</span><span style="FONT-FAMILY: 楷体_GB2312">用法规则不鼓励采用所谓的</span><span lang="EN-US">Hungarian</span><span style="FONT-FAMILY: 楷体_GB2312">表示法,在名称的前面加一个字母,表示对象的类型,接口是</span><span lang="EN-US">Hungarian</span><span style="FONT-FAMILY: 楷体_GB2312">表示法推荐采用的几种名称之一。</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">现在可以编写表示银行账户的类了。这些类不必彼此相关,它们可以是完全不同的类。但它们都表示银行账户,因为它们都实现</span><span lang="EN-US">IbankAccount</span><span style="FONT-FAMILY: 宋体">接口。</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">下面是第一个类,一个由</span><span lang="EN-US">Royal Bank of Venus</span><span style="FONT-FAMILY: 宋体">运行的存款账户:</span></p>
<p class="2" style="MARGIN: 8.15pt 0cm 0pt 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">namespace Wrox.ProCSharp.VenusBank</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">{</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp; public class SaverAccount : IBankAccount</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp; {</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private decimal balance;</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void PayIn(decimal amount)</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; balance += amount;</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public bool Withdraw(decimal amount)</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (balance &gt;= amount)</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; balance -= amount;</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true;</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine(&quot;Withdrawal attempt failed.&quot;);</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public decimal Balance</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; get</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return balance;</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public override string ToString()</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return String.Format(&quot;Venus Bank Saver: Balance = {0,6:C}&quot;, balance);</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p class="2" style="MARGIN-LEFT: 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">&nbsp;&nbsp; }</span></p>
<p class="2" style="MARGIN: 0cm 0cm 8.15pt 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US">}</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">这个类的实现一目了然。其中包含一个私有字段</span><span lang="EN-US">balance</span><span style="FONT-FAMILY: 宋体">,当存款或取款时就调整这个字段。如果因为账户中的金额不足而取款失败,就会显示一个错误消息。还要注意,因为我们要使代码尽可能简单,所以不实现额外的属性,例如账户持有人的姓名。在现实生活中,这是最基本的信息,但对于本例来说,这是不必要的。</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">在这段代码中,惟一有趣的是类的声明:</span></p>
<p class="a6" style="BACKGROUND: #f2f2f2; MARGIN: 8.15pt 0cm 8.15pt 21.45pt; TEXT-INDENT: 18.45pt"><span lang="EN-US" style="BACKGROUND: #f2f2f2">public class SaverAccount : IBankAccount</span></p>
<p class="MsoNormal"><span lang="EN-US">SaverAccount</span><span style="FONT-FAMILY: 宋体">派生于一个接口</span><span lang="EN-US">IbankAccount</span><span style="FONT-FAMILY: 宋体">,我们没有明确指出任何其他基类</span><span lang="EN-US">(</span><span style="FONT-FAMILY: 宋体">当然这表示</span><span lang="EN-US">SaverAccount</span><span style="FONT-FAMILY: 宋体">直接派生于</span><span lang="EN-US">System.Object)</span><span style="FONT-FAMILY: 宋体">。另外,从接口中派生完全独立于从类中派生。</span></p>
<p class="MsoNormal"><span lang="EN-US">SaverAccount</span><span style="FONT-FAMILY: 宋体">派生于</span><span lang="EN-US">IbankAccount</span><span style="FONT-FAMILY: 宋体">,表示它获得了</span><span lang="EN-US">IbankAccount</span><span style="FONT-FAMILY: 宋体">的所有成员,但接口并不实际实现其方法,所以</span><span lang="EN-US">SaverAccount</span><span style="FONT-FAMILY: 宋体">必须提供这些方法的所有实现。如果没有提供实现,编译器就会产生错误。接口仅表示其成员的存在性,类负责确定这些成员是虚拟还是抽象的</span><span lang="EN-US">(</span><span style="FONT-FAMILY: 宋体">但只有在类本身是抽象的,这些成员才能是抽象的</span><span lang="EN-US">)</span><span style="FONT-FAMILY: 宋体">。在本例中,接口方法不必是虚拟的。</span></p>
<p class="MsoNormal"><span style="FONT-FAMILY: 宋体">为了说明不同的类如何实现相同的接口,下面假定</span><span lang="EN-US">Planetary Bank of Jupiter</span><span style="FONT-FAMILY: 宋体">还实现一个类</span><span lang="EN-US">Gold Account</span><span style="FONT-FAMILY: 宋体">来表示其银行账户:</span></p>

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?