当前位置:首页 > 网络编程 > WEB编程 > ASP.net > <展现C#> 第五章 类 (2)(rainbow 翻译)

<展现C#> 第五章 类 (2)(rainbow 翻译)

点击次数:21 次 发布日期:2008-11-27 00:10:12 作者:源代码网
源代码网推荐
源代码网推荐 第五章 类 (2)

5.3 类属性
源代码网推荐 有两种途径揭示类的命名属性——通过域成员或者通过属性。前者是作为具有公共访问性的成员变量而被实现的;后
源代码网推荐者并不直接回应存储位置,只是通过 存取标志(accessors)被访问。
源代码网推荐 当你想读出或写入属性的值时,存取标志限定了被实现的语句。用于读出属性的值的存取标志记为关键字get,而要修
源代码网推荐改属性的值的读写符标志记为set。
源代码网推荐在你对该理论一知半解以前,请看一下清单5.9中的例子,属性SquareFeet被标上了get和set的存取标志。
源代码网推荐清单 5.9 实现属性存取标志

1: using System;
源代码网推荐2:
源代码网推荐3: public class House
源代码网推荐4: {
源代码网推荐5: private int m_nSqFeet;
源代码网推荐6:
源代码网推荐7: public int SquareFeet
源代码网推荐8: {
源代码网推荐9: get { return m_nSqFeet; }
源代码网推荐10: set { m_nSqFeet = value; }
源代码网推荐11: }
源代码网推荐12: }
源代码网推荐13:
源代码网推荐14: class TestApp
源代码网推荐15: {
源代码网推荐16: public static void Main()
源代码网推荐17: {
源代码网推荐18: House myHouse = new House();
源代码网推荐19: myHouse.SquareFeet = 250;
源代码网推荐20: Console.WriteLine(myHouse.SquareFeet);
源代码网推荐21: }
源代码网推荐22: }

House类有一个命名为SquareFeet的属性,它可以被读和写。实际的值存储在一个可以从类内部访问的变量中——如果
源代码网推荐你想当作一个域成员重写它,你所要做的就是忽略存取标志而把变量重新定义为:
源代码网推荐public int SquareFeet;
源代码网推荐对于一个如此简单的变量,这样不错。但是,如果你想要隐藏类内部存储结构的细节时,就应该采用存取标志。在这种情
源代码网推荐况下,set 存取标志给值参数中的属性传递新值。(可以改名,见第10行。)
源代码网推荐除了能够隐藏实现细节外,你还可自由地限定各种操作:
源代码网推荐get和set:允许对属性进行读写访问。
源代码网推荐get only:只允许读属性的值。
源代码网推荐set only:只允许写属性的值。
源代码网推荐除此之外,你可以获得实现在set标志中有效代码的机会。例如,由于种种原因(或根本没有原因),你就能够拒绝一个新
源代码网推荐值。最好是没有人告诉你它是一个动态属性——当你第一次请求它后,它会保存下来,故要尽可能地推迟资源分配。

5.4 索引
源代码网推荐 你想过象访问数组那样使用索引访问类吗 ?使用C#的索引功能,对它的期待便可了结。

语法基本上象这样:
源代码网推荐属性 修饰符 声明 { 声明内容}

具体的例子为
源代码网推荐public string this[int nIndex]
源代码网推荐{
源代码网推荐get { ... }
源代码网推荐set { ... }
源代码网推荐}

索引返回或按给出的index设置字符串。它没有属性,但使用了public修饰符。声明部分由类型string和this 组成用于表
源代码网推荐示类的索引。
源代码网推荐get和set的执行规则和属性的规则相同。(你不能取消其中一个。) 只存在一个差别,那就是:你几乎可以任意定义大括
源代码网推荐弧中的参数。限制为,必须至少规定一个参数,允许ref 和out 修饰符。
源代码网推荐this关键字确保一个解释。索引没有用户定义的名字,this 表示默认接口的索引。如果类实现了多个接口,你可以增加更
源代码网推荐多个由InterfaceName.this说明的索引。

为了演示一个索引的使用,我创建了一个小型的类,它能够解析一个主机名为IP地址——或一个IP地址列表(以
源代码网推荐http://www.microsoft.com为例 )。这个列表通过索引可以访问,你可以看一下清单5.10 的具体实现。

清单 5.10 通过一个索引获取一个IP地址

1: using System;
源代码网推荐2: using System.Net;
源代码网推荐3:
源代码网推荐4: class ResolveDNS
源代码网推荐5: {
源代码网推荐6: IPAddress[] m_arrIPs;
源代码网推荐7:
源代码网推荐8: public void Resolve(string strHost)
源代码网推荐9: {
源代码网推荐10: IPHostEntry iphe = DNS.GetHostByName(strHost);
源代码网推荐11: m_arrIPs = iphe.AddressList;
源代码网推荐12: }
源代码网推荐13:
源代码网推荐14: public IPAddress this[int nIndex]
源代码网推荐15: {
源代码网推荐16: get
源代码网推荐17: {
源代码网推荐18: return m_arrIPs[nIndex];
源代码网推荐19: }
源代码网推荐20: }
源代码网推荐21:
源代码网推荐22: public int Count
源代码网推荐23: {
源代码网推荐24: get { return m_arrIPs.Length; }
源代码网推荐25: }
源代码网推荐26: }
源代码网推荐27:
源代码网推荐28: class DNSResolverApp
源代码网推荐29: {
源代码网推荐30: public static void Main()
源代码网推荐31: {
源代码网推荐32: ResolveDNS myDNSResolver = new ResolveDNS();
源代码网推荐33: myDNSResolver.Resolve("http://www.microsoft.com");
源代码网推荐34:
源代码网推荐35: int nCount = myDNSResolver.Count;
源代码网推荐36: Console.WriteLine("Found {0} IP"s for hostname", nCount);
源代码网推荐37: for (int i=0; i &lt; nCount; i++)
源代码网推荐38: Console.WriteLine(myDNSResolver[i]);
源代码网推荐39: }
源代码网推荐40: }

为了解析主机名,我用到了DNS类,它是System .Net 名字空间的一部分。但是,由于这个名字空间并不包含在核心
源代码网推荐库中,所以必须在编译命令行中引用该库:
源代码网推荐csc /r:System.Net.dll /out:resolver.exe dnsresolve.cs
源代码网推荐 解析代码是向前解析的。在该 Resolve方法中,代码调用DNS类的静态方法GetHostByName,它返回一个IPHostEntry
源代码网推荐对象。结果,该对象包含有我要找的数组——AddressList数组。在退出Resolve 方法之前,在局部的对象实例成员
源代码网推荐m_arrIPs中,存储了一个AddressList array的拷贝(类型IPAddress 的对象存储在其中)。
源代码网推荐 具有现在生成的数组 ,通过使用在类ResolveDNS中求得的索引,应用程序代码就可以在第37至38行列举出IP地址。
源代码网推荐(在第6章 "控制语句",有更多有关语句的信息。) 因为没有办法更改IP地址,所以仅给索引使用了get存取标志。为了
源代码网推荐简单其见,我忽略了数组的边界溢出检查。
源代码网推荐
源代码网推荐5.4 事件
源代码网推荐 当你写一个类时,有时有必要让类的客户知道一些已经发生的事件。如果你是一个具有多年编程经验的程序员,似乎有
源代码网推荐很多的解决办法,包括用于回调的函数指针和用于ActiveX控件的事件接收(event sinks)。现在你将要学到另外一种把客
源代码网推荐户代码关联到类通知的办法——使用事件。
源代码网推荐 事件既可以被声明为类域成员(成员变量),也可以被声明为属性。两者的共性为,事件的类型必定是代表元,而函
源代码网推荐数指针原形和C#的代表元具有相同的含义。
源代码网推荐 每一个事件都可以被0或更多的客户占用,且客户可以随时关联或取消事件。你可以以静态或者以实例方法定义代表
源代码网推荐元,而后者很受C++程序员的欢迎。
源代码网推荐 既然我已经提到了事件的所有功能及相应的代表元,请看清单5.11中的例子。它生动地体现了该理论。

清单5.11 在类中实现事件处理
源代码网推荐1: using System;
源代码网推荐2:
源代码网推荐3: // 向前声明
源代码网推荐4: public delegate void EventHandler(string strText);
源代码网推荐5:
源代码网推荐6: class EventSource
源代码网推荐7: {
源代码网推荐8: public event EventHandler TextOut;
源代码网推荐9:
源代码网推荐10: public void TriggerEvent()
源代码网推荐11: {
源代码网推荐12: if (null != TextOut) TextOut("Event triggered");
源代码网推荐13: }
源代码网推荐14: }
源代码网推荐15:
源代码网推荐16: class TestApp
源代码网推荐17: {
源代码网推荐18: public static void Main()
源代码网推荐19: {
源代码网推荐20: EventSource evsrc = new EventSource();
源代码网推荐21:
源代码网推荐22: evsrc.TextOut += new EventHandler(CatchEvent);
源代码网推荐23: evsrc.TriggerEvent();
源代码网推荐24:
源代码网推荐25: evsrc.TextOut -= new EventHandler(CatchEvent);
源代码网推荐26: evsrc.TriggerEvent();
源代码网推荐27:
源代码网推荐28: TestApp theApp = new TestApp();
源代码网推荐29: evsrc.TextOut += new EventHandler(theApp.InstanceCatch);
源代码网推荐30: evsrc.TriggerEvent();
源代码网推荐31: }
源代码网推荐32:
源代码网推荐33: public static void CatchEvent(string strText)
源代码网推荐34: {
源代码网推荐35: Console.WriteLine(strText);
源代码网推荐36: }
源代码网推荐37:
源代码网推荐38: public void InstanceCatch(string strText)
源代码网推荐39: {
源代码网推荐40: Console.WriteLine("Instance " + strText);
源代码网推荐41: }
源代码网推荐42: }

第4行声明了代表元(事件方法原形),它用来给第8行中的EventSource类声明TextOut事件域成员。你可以观察到代
源代码网推荐表元作为一种新的类型声明,当声明事件时可以使用代表元。
源代码网推荐 该类仅有一个方法,它允许我们触发事件。请注意,你必须进行事件域成员不为null的检测,因为可能会出现没有客
源代码网推荐户对事件感兴趣这种情况。
源代码网推荐 TestApp类包含了Main 方法,也包含了另外两个方法,它们都具备事件所必需的信号。其中一个方法是静态的,而另
源代码网推荐一个是实例方法。
源代码网推荐 EventSource 被实例化,而静态方法CatchEvent被预关联上了 TextOut事件:
源代码网推荐evsrc.TextOut += new EventHandler(CatchEvent);
源代码网推荐 从现在起,当事件被触发时,该方法被调用。如果你对事件不再感兴趣,简单地取消关联:
源代码网推荐evsrc.TextOut -= new EventHandler(CatchEvent);
源代码网推荐 注意,你不能随意取消关联的处理函数——在类代码中仅创建了这些处理函数。为了证明事件处理函数也和实例方法
源代码网推荐一起工作,余下的代码建立了TestApp 的实例,并钩住事件处理方法。
源代码网推荐 事件在哪方面对你特别有用?你将经常在ASP+中或使用到WFC (Windows Foundation Classes)时,涉及到事件和代表
源代码网推荐元。

5.5 应用修饰符
源代码网推荐 在这一章的学习过程中,你已经见过了象public、virtual等修饰符。欲以一种易于理解的方法概括它们,我把它们划
源代码网推荐分为三节:

。类修饰符
源代码网推荐。成员修饰符
源代码网推荐。存取修饰符

5.5.1 类修饰符
源代码网推荐 到目前为止,我还没有涉及到类修饰符,而只涉及到了应用于类的存取修饰符。但是,有两个修饰符你可以用于类:
源代码网推荐 abstract——关于抽象类的重要一点就是它不能被实例化。只有不是抽象的派生类才能被实例化。派生类必须实现抽
源代码网推荐象基类的所有抽象成员。你不能给抽象类使用sealed 修饰符。
源代码网推荐 sealed——密封 类不能被继承。使用该修饰符防止意外的继承,在.NET框架中的类用到这个修饰符。
源代码网推荐 要见到两个修饰符的运用,看看清单5.12 ,它创建了一个基于一个抽象类的密封类(肯定是一个十分极端的例子)。

清单 5.12 抽象类和密封类

1: using System;
源代码网推荐2:
源代码网推荐3: abstract class AbstractClass
源代码网推荐4: {
源代码网推荐5: abstract public void MyMethod();
源代码网推荐6: }
源代码网推荐7:
源代码网推荐8: sealed class DerivedClass:AbstractClass
源代码网推荐9: {
源代码网推荐10: public override void MyMethod()
源代码网推荐11: {
源代码网推荐12: Console.WriteLine("sealed class");
源代码网推荐13: }
源代码网推荐14: }
源代码网推荐15:
源代码网推荐16: public class TestApp
源代码网推荐17: {
源代码网推荐18: public static void Main()
源代码网推荐19: {
源代码网推荐20: DerivedClass dc = new DerivedClass();
源代码网推荐21: dc.MyMethod();
源代码网推荐22: }
源代码网推荐23: }

5.5.2 成员修饰符
源代码网推荐 与有用的成员修饰符的数量相比,类修饰符的数量很少。我已经提到了一些,这本书即将出现的例子描述了其它的成
源代码网推荐员修饰符。
源代码网推荐 以下是有用的成员修饰符:
源代码网推荐 abstract——说明一个方法或存取标志不能含有一个实现。它们都是隐式虚拟,且在继承类中,你必须提供
源代码网推荐override关键字。
源代码网推荐 const——这个修饰符应用于域成员或局部变量。在编译时常量表达式被求值,所以,它不能包含变量的引用。
源代码网推荐 event ——定义一个域成员或属性作为类型事件。用于捆绑客户代码到类的事件。
源代码网推荐 extern——告诉编译器方法实际上由外部实现。第10章 “和非受管代码互相操作” 将全面地涉及到外部代码。
源代码网推荐 override——用于改写任何基类中被定义为virtual的方法和存取标志。要改写的名字和基类的方法必须一致。
源代码网推荐 readonly——一个使用 readonly修饰符的域成员只能在它的声明或者在包含它的类的构造函数中被更改。

static——被声明为static的成员属于类,而不属于类的实例。你可以用static 于域成员、方法、属性、操作符甚至
源代码网推荐构造函数。
源代码网推荐 virtual——说明方法或存取标志可以被继承类改写。

5.5.3 存取修饰符
源代码网推荐 存取修饰符定义了某些代码对类成员(如方法和属性)的存取等级。你必须给每个成员加上所希望的存取修饰符,否
源代码网推荐则,默认的存取类型是隐含的。
源代码网推荐 你可以应用4个 存取修饰符之一:
源代码网推荐 public——任何地方都可以访问该成员,这是具有最少限制的存取修饰符。
源代码网推荐 protected——在类及所有的派生类中可以访问该成员,不允许外部访问。
源代码网推荐 private——仅仅在同一个类的内部才能访问该成员。甚至派生类都不能访问它。
源代码网推荐 internal——允许相同组件(应用程序或库)的所有代码访问。在.NET组件级别,你可以把它视为public,而在外部
源代码网推荐则为private。
源代码网推荐 为了演示存取修饰符的用法,我稍微修改了Triangle例子,使它包含了新增的域成员和一个新的派生类(见清单
源代码网推荐5.13)。

清单 5.13 在类中使用存取修饰符

1: using System;
源代码网推荐2:
源代码网推荐3: internal class Triangle
源代码网推荐4: {
源代码网推荐5: protected int m_a, m_b, m_c;
源代码网推荐6: public Triangle(int a, int b, int c)
源代码网推荐7: {
源代码网推荐8: m_a = a;
源代码网推荐9: m_b = b;
源代码网推荐10: m_c = c;
源代码网推荐11: }
源代码网推荐12:
源代码网推荐13: public virtual double Area()
源代码网推荐14: {
源代码网推荐15: // Heronian formula
源代码网推荐16: double s = (m_a + m_b + m_c) / 2.0;
源代码网推荐17: double dArea = Math.Sqrt(s*(s-m_a)*(s-m_b)*(s-m_c));
源代码网推荐18: return dArea;
源代码网推荐19: }
源代码网推荐20: }
源代码网推荐21:
源代码网推荐22: internal class Prism:Triangle
源代码网推荐23: {
源代码网推荐24: private int m_h;
源代码网推荐25: public Prism(int a, int b, int c, int h):base(a,b,c)
源代码网推荐26: {
源代码网推荐27: m_h = h;
源代码网推荐28: }
源代码网推荐29:
源代码网推荐30: public override double Area()
源代码网推荐31: {
源代码网推荐32: double dArea = base.Area() * 2.0;
源代码网推荐33: dArea += m_a*m_h + m_b*m_h + m_c*m_h;
源代码网推荐34: return dArea;
源代码网推荐35: }
源代码网推荐36: }
源代码网推荐37:
源代码网推荐38: class PrismApp
源代码网推荐39: {
源代码网推荐40: public static void Main()
源代码网推荐41: {
源代码网推荐42: Prism prism = new Prism(2,5,6,1);
源代码网推荐43: Console.WriteLine(prism.Area());
源代码网推荐44: }
源代码网推荐45: }

Triangle 类和 Prism 类现在被标为 internal。这意味着它们只能在当前组件中被访问。请记住“.NET组件”这
源代码网推荐个术语指的是包装( packaging,),而不是你可能在COM+中用到的组件。Triangle 类有三个 protected成员,它们在构
源代码网推荐造函数中被初始化,并用于面积计算的方法中。由于这些成员是protected 成员,所以我可以在派生类Prism中访问它们,
源代码网推荐在那里执行不同的面积计算。Prism自己新增了一个成员m_h,它是私有的——甚至派生类也不能访问它。
源代码网推荐 花些时间为每个类成员甚至每个类计划一种保护层次,通常是个好主意。当需要引入修改时,全面的计划最终会帮
源代码网推荐助你,因为没有程序员会愿意使用“没有文档”的类功能。
源代码网推荐5.6 小结
源代码网推荐 这章显示了类的各种要素,它是运行实例(对象)的模板。在一个对象的生命期,首先被执行的代码是个构造函数。
源代码网推荐构造函数用来初始化变量,这些变量后来在方法中用于计算结果。
源代码网推荐 方法允许你传递值、引用给变量,或者只传送一个输出值。方法可以被改写以实现新的功能,或者你可以屏蔽基类成
源代码网推荐员,如果它实现了一个具有和派生类成员相同名字的方法。
源代码网推荐 命名属性可以被当作域成员(成员变量)或属性存取标志实现。后者是get和set存取标志,忽略一个或另外一个,你
源代码网推荐可以创建仅写或仅读属性。存取标志非常适合于确认赋给属性的值。
源代码网推荐 C#类的另外一个功能是索引,它使象数组语法一样访问类中值成为可能。还有,如果当类中的某些事情发生时,你想
源代码网推荐客户得到通知,要让它们与事件关联。
源代码网推荐 当垃圾收集器调用析构函数时,对象的生命就结束了。由于你不能准确地预测这种情况什么时候会发生,所以应该创
源代码网推荐建一个方法以释放这些宝贵的资源,当你停止使用它们时。
源代码网推荐
源代码网供稿.

网友评论 (0)
会员中心
网络编程
本站推荐
网络编程之精华