⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 《_net编程先锋c#》第五章 类_c#语言_网络教程.htm

📁 最近在研究c#
💻 HTM
📖 第 1 页 / 共 3 页
字号:
            Release()<BR>{<BR>// 释放所有宝贵的资源<BR>} <BR><BR>public 
            ~TestClass()<BR>{<BR>Release();<BR>} 
            <BR><BR>调用析构函数中的释放方法并不是必要的——总之,垃圾收集会留意释放对象。但没有忘记清 <BR><BR>除是一种良好的习惯。 
            <BR><BR>5.2 方法<BR>既然对象能正确地初始化和结束,所剩下来的就是往类中增加功能。在大多数情况下,功能的 
            <BR><BR>主要部分在方法中能得到实现。你早已见过静态方法的使用,但是,这些是类型(类)的部分,不是 
            <BR><BR>实例(对象)。<BR>为了让你迅速入门,我把这些方法的烦琐问题安排为三节:<BR>。方法参数<BR>。改写方法<BR>。方法屏蔽<BR>5.2.1 
            方法参数<BR>因方法要处理更改数值,你多多少少要传递值给方法,并从方法获得返回值。以下三个部分涉及 
            <BR><BR>到由传递值和为调用者获取返回结果所引起的问题。 <BR><BR>。输入参数<BR>。引用参数<BR>。输出参数 
            <BR><BR>5.2.1.1 输入参数<BR>你早已在例子中见过的一个参数就是输入参数。你用一个输入参数通过值传递一个变量给一个方 
            <BR><BR>法——方法的变量被调用者传递进来的值的一个拷贝初始化。清单5.1 示范输入参数的使用。 <BR><BR>清单 5.1 
            通过值传递参数 <BR><BR>1: using System;<BR>2: <BR>3: public class 
            SquareSample<BR>4: {<BR>5: public int CalcSquare(int 
            nSideLength)<BR>6: {<BR>7: return nSideLength*nSideLength;<BR>8: 
            }<BR>9: }<BR>10: <BR>11: class SquareApp<BR>12: {<BR>13: public 
            static void Main()<BR>14: {<BR>15: SquareSample sq = new 
            SquareSample();<BR>16: 
            Console.WriteLine(sq.CalcSquare(25).ToString());<BR>17: }<BR>18: } 
            <BR><BR>因为我传递值而不是引用给一个变量,所以当调用方法时(见第16行),可以使用一个常量表达式 
            <BR><BR>(25)。整型结果被传回给调用者作为返回值,它没有存到中间变量就被立即显示到屏幕上 
            。<BR>输入参数按C/C++程序员早已习惯的工作方式工作。如果你来自VB,请注意没有能被编译器处理 
            <BR><BR>的隐式ByVal或ByRef——如果没有设定,参数总是用值传递。<BR>这点似乎与我前面所陈述的有冲突:对于一些变量类型,用值传递实际上意味着用引用传递。 
            <BR><BR>迷惑吗? 一点背景知识也不需要:COM中的东西就是接口,每一个类可以拥有一个或多个接口。一个 
            <BR><BR>接口只不过是一组函数指针,它不包含数据。重复该数组会浪费很多内存资源;所以,仅开始地址 
            <BR><BR>被拷贝给方法,它作为调用者,仍然指向接口的相同指针。那就是为什么对象用值传递一个引用。 <BR><BR>5.2.1.2 
            引用参数<BR>尽管可以利用输入参数和返回值建立很多方法,但你一想到要传递值并原地修改它(也就是在相 
            <BR><BR>同的内存位置),就没有那么好运了。这里用引用参数就很方便。<BR>void myMethod(ref int 
            nInOut)<BR>因为你传递了一个变量给该方法(不仅仅是它的值),变量必须被初始化。否则,编译器会报警。 <BR><BR>清单 
            5.2 显示如何用一个引用参数建立一个方法。 <BR><BR>清单 5.2 通过引用传递参数 <BR><BR>1: // class 
            SquareSample<BR>2: using System;<BR>3: <BR>4: public class 
            SquareSample<BR>5: {<BR>6: public void CalcSquare(ref int 
            nOne4All)<BR>7: {<BR>8: nOne4All *= nOne4All;<BR>9: }<BR>10: 
            }<BR>11: <BR>12: class SquareApp<BR>13: {<BR>14: public static void 
            Main()<BR>15: {<BR>16: SquareSample sq = new SquareSample();<BR>17: 
            <BR>18: int nSquaredRef = 20; // 一定要初始化<BR>19: sq.CalcSquare(ref 
            nSquaredRef);<BR>20: 
            Console.WriteLine(nSquaredRef.ToString());<BR>21: }<BR>22: } 
            <BR><BR>正如所看到的,所有你要做的就是给定义和调用都加上ref限定符。因为变量通过引用传递,你 
            <BR><BR>可以用它来计算出结果并传回该结果。但是,在现实的应用程序中,我强烈建议要用两个变量,一 
            <BR><BR>个输入参数和一个引用参数。 <BR><BR>5.2.1.3 
            输出参数<BR>传递参数的第三种选择就是把它设作一个输出参数。正如该名字所暗示,一个输出参数仅用于从 
            <BR><BR>方法传递回一个结果。它和引用参数的另一个区别在于:调用者不必先初始化变量才调用方法。这 
            <BR><BR>显示在清单5.3中。 <BR><BR>清单 5.3 定义一个输出参数 <BR><BR>1: using 
            System;<BR>2: <BR>3: public class SquareSample<BR>4: {<BR>5: public 
            void CalcSquare(int nSideLength, out int nSquared)<BR>6: {<BR>7: 
            nSquared = nSideLength * nSideLength;<BR>8: }<BR>9: }<BR>10: <BR>11: 
            class SquareApp<BR>12: {<BR>13: public static void Main()<BR>14: 
            {<BR>15: SquareSample sq = new SquareSample();<BR>16: <BR>17: int 
            nSquared; // 不必初始化<BR>18: sq.CalcSquare(15, out nSquared);<BR>19: 
            Console.WriteLine(nSquared.ToString());<BR>20: }<BR>21: } 
            <BR><BR><BR>5.2.2 改写方法<BR>面向对象设计的重要原则就是多态性。不要理会高深的理论,多态性意味着:当基类程序员已 
            <BR><BR>设计好用于改写的方法时,在派生类中,你就可以重定义(改写)基类的方法。基类程序员可以用 <BR><BR>virtual 
            关键字设计方法:<BR>virtual void 
            CanBOverridden()<BR>当从基类派生时,所有你要做的就是在新方法中加入override关键字:<BR>override 
            void CanBOverridden()<BR>当改写一个基类的方法时,你必须明白,不能改变方法的访问属性——在这章的后面,你会学 
            <BR><BR>到更多关于访问修饰符的知识。<BR>除了改写基类方法的事实外,还有另一个甚至更重要的改写特性。当把派生类强制转换成基类 
            <BR><BR>类型并接着调用虚拟方法时,被调用的是派生类的方法而不是基类的方法。<BR>((BaseClass)DerivedClassInstance).CanBOverridden();<BR>为了演示虚拟方法的概念,清单 
            5.4 显示如何创建一个三角形基类,它拥有一个可以被改 <BR><BR>写的成员方法(ComputeArea)。 <BR><BR>清单 
            5.4 改写一个基类的方法 <BR><BR>1: using System;<BR>2: <BR>3: class 
            Triangle<BR>4: {<BR>5: public virtual double ComputeArea(int a, int 
            b, int c)<BR>6: {<BR>7: // Heronian formula<BR>8: double s = (a + b 
            + c) / 2.0;<BR>9: double dArea = 
            Math.Sqrt(s*(s-a)*(s-b)*(s-c));<BR>10: return dArea;<BR>11: }<BR>12: 
            }<BR>13: <BR>14: class RightAngledTriangle:Triangle<BR>15: {<BR>16: 
            public override double ComputeArea(int a, int b, int c)<BR>17: { 
            <BR>18: double dArea = a*b/2.0;<BR>19: return dArea;<BR>20: }<BR>21: 
            }<BR>22: <BR>23: class TriangleTestApp<BR>24: {<BR>25: public static 
            void Main()<BR>26: {<BR>27: Triangle tri = new Triangle();<BR>28: 
            Console.WriteLine(tri.ComputeArea(2, 5, 6));<BR>29: <BR>30: 
            RightAngledTriangle rat = new RightAngledTriangle();<BR>31: 
            Console.WriteLine(rat.ComputeArea(3, 4, 5));<BR>32: }<BR>33: } 
            <BR><BR>基类Triangle定义了方法ComputeArea。它采用三个参数,返回一个double结果,且具有公共访 
            <BR><BR>问性。从Triangle类派生出的是RightAngledTriangle,它改写了ComputeArea 
            方法,并实现了自己 <BR><BR>的面积计算公式。两个类都被实例化,且在命名为TriangleTestApp的应用类的Main() 
            方法中得到 <BR><BR>验证。<BR>我漏了解释第14行:<BR>class RightAngledTriangle : 
            Triangle<BR>在类语句中冒号(:)表示RightAngledTriangle从类 Triangle派生。那就是你所必须要做的 
            <BR><BR>,以让C#知道你想把 
            Triangle当作RightAngledTriangle的基类。<BR>当仔细观察直角三角形的ComputeArea方法时,你会发现第3个参数并没有用于计算。但是,利 
            <BR><BR>用该参数就可以验证是否是“直角”。如清单5.5所示。 <BR><BR>清单 5.5 调用基类实现 <BR><BR>1: 
            class RightAngledTriangle:Triangle<BR>2: {<BR>3: public override 
            double ComputeArea(int a, int b, int c)<BR>4: {<BR>5: const double 
            dEpsilon = 0.0001;<BR>6: double dArea = 0;<BR>7: if (Math.Abs((a*a + 
            b*b - c*c)) &gt; dEpsilon)<BR>8: {<BR>9: dArea = 
            base.ComputeArea(a,b,c);<BR>10: }<BR>11: else<BR>12: {<BR>13: dArea 
            = a*b/2.0;<BR>14: }<BR>15: <BR>16: return dArea;<BR>17: }<BR>18: } 
            <BR><BR>该检测简单地利用了毕达哥拉斯公式,对于直角三角形,检测结果必须为0。如果结果不为0,类 <BR><BR>就调用它基类的 
            ComputeArea来实现。<BR>dArea = 
            base.ComputeArea(a,b,c);<BR>例子的要点为:通过显式地利用基类的资格检查,你就能轻而易举地调用基类实现改写方法。<BR>当你需要实现其在基类中的功能,而不愿意在改写方法中重复它时,这就非常有帮助。 
            <BR><BR>5.2.3 方法屏蔽<BR>重定义方法的一个不同手段就是要屏蔽基类的方法。当从别人提供的类派生类时,这个功能特 
            <BR><BR>别有价值。看清单 5.6,假设BaseClass由其他人所写,而你从它派生出 DerivedClass 。 
            <BR><BR>清单 5.6 Derived Class 实现一个没有包含于 Base Class中的方法 <BR><BR>1: 
            using System;<BR>2: <BR>3: class BaseClass<BR>4: {<BR>5: }<BR>6: 
            <BR>7: class DerivedClass:BaseClass<BR>8: {<BR>9: public void 
            TestMethod()<BR>10: {<BR>11: 
            Console.WriteLine("DerivedClass::TestMethod");<BR>12: }<BR>13: 
            }<BR>14: <BR>15: class TestApp<BR>16: {<BR>17: public static void 
            Main()<BR>18: {<BR>19: DerivedClass test = new 
            DerivedClass();<BR>20: test.TestMethod();<BR>21: }<BR>22: } 
            <BR><BR>在这个例子中, DerivedClass 通过TestMethod()实现了一个额外的功能。但是,如果基类的 
            <BR><BR>开发者认为把TestMethod()放在基类中是个好主意,并使用相同的名字实现它时,会出现什么问题 
            <BR><BR>呢?(见清单5.7) <BR><BR>清单 5.7 Base Class 实现和 Derived Class相同的方法 
            <BR><BR>1: class BaseClass<BR>2: {<BR>3: public void 
            TestMethod()<BR>4: {<BR>5: 
            Console.WriteLine("BaseClass::TestMethod");<BR>6: }<BR>7: }<BR>8: 
            <BR>9: class DerivedClass:BaseClass<BR>10: {<BR>11: public void 
            TestMethod()<BR>12: {<BR>13: 
            Console.WriteLine("DerivedClass::TestMethod");<BR>14: }<BR>15: } 
            <BR><BR>在优秀的编程语言中,你现在会遇到一个真正的大麻烦。但是,C#会给你提出警告:<BR>hiding2.cs(13,14): 
            warning CS0114: 'DerivedClass.TestMethod()' hides inherited member 
            <BR><BR>'BaseClass.TestMethod()'. To make the current method 
            override that implementation, add <BR><BR>the override keyword. 
            Otherwise add the new keyword.<BR>(hiding2.cs(13,14):警告 
            CS0114:'DerivedClass.TestMethod()' 屏蔽了所继承的成员 
            <BR><BR>'BaseClass.TestMethod()'。要想使当前方法改写原来的实现,加上 override关键字。否则加上新 
            <BR><BR>的关键字。)<BR>具有了修饰符new,你就可以告诉编译器,不必重写派生类或改变使用到派生类的代码,你的方法就 
            <BR><BR>能屏蔽新加入的基类方法。清单5.8 显示如何在例子中运用new修饰符。 <BR><BR>清单 5.8 屏蔽基类方法 
            <BR><BR>1: class BaseClass<BR>2: {<BR>3: public void 
            TestMethod()<BR>4: {<BR>5: 
            Console.WriteLine("BaseClass::TestMethod");<BR>6: }<BR>7: }<BR>8: 
            <BR>9: class DerivedClass:BaseClass<BR>10: {<BR>11: new public void 
            TestMethod()<BR>12: {<BR>13: 
            Console.WriteLine("DerivedClass::TestMethod");<BR>14: }<BR>15: } 
            <BR><BR>使用了附加的new修饰符,编译器就知道你重定义了基类的方法,它应该屏蔽基类方法。但是,如果 
            <BR><BR>你按以下方式编写:<BR>DerivedClass test = new 
            DerivedClass();<BR>((BaseClass)test).TestMethod();<BR>基类方法的实现就被调用了。这种行为不同于改写方法,后者保证大部分派生方法获得调用。<BR>5.3 
            类属性<BR>有两种途径揭示类的命名属性——通过域成员或者通过属性。前者是作为具有公共访问性的成员变量而被实现的;后者并不直接回应存储位置,只是通过 
            存取标志(accessors)被访问。<BR>当你想读出或写入属性的值时,存取标志限定了被实现的语句。用于读出属性的值的存取标志记为关键字get,而要修改属性的值的读写符标志记为set。在你对该理论一知半解以前,请看一下清单5.9中的例子,属性SquareFeet被标上了get和set的存取标志。<BR>清单 
            5.9 实现属性存取标志 <BR>1: using System;<BR>2: <BR>3: public class 
            House<BR>4: {<BR>5: private int m_nSqFeet;<BR>6: <BR>7: public int 
            SquareFeet<BR>8: {<BR>9: get { return m_nSqFeet; }<BR>10: set { 
            m_nSqFeet = value; }<BR>11: }<BR>12: }<BR>13: <BR>14: class 
            TestApp<BR>15: {<BR>16: public static void Main()<BR>17: {<BR>18: 
            House myHouse = new House();<BR>19: myHouse.SquareFeet = 250;<BR>20: 
            Console.WriteLine(myHouse.SquareFeet);<BR>21: }<BR>22: } 
            <BR><BR>House类有一个命名为SquareFeet的属性,它可以被读和写。实际的值存储在一个可以从类内部访问的变量中——如果你想当作一个域成员重写它,你所要做的就是忽略存取标志而把变量重新定义为:<BR>public 
            int 
            SquareFeet;<BR>对于一个如此简单的变量,这样不错。但是,如果你想要隐藏类内部存储结构的细节时,就应该采用存取标志。在这种情况下,set 
            存取标志给值参数中的属性传递新值。(可以改名,见第10行。)<BR>除了能够隐藏实现细节外,你还可自由地限定各种操作:<BR>get和set:允许对属性进行读写访问。<BR>get 
            only:只允许读属性的值。<BR>set 
            only:只允许写属性的值。<BR>除此之外,你可以获得实现在set标志中有效代码的机会。例如,由于种种原因(或根本没有原因),你就能够拒绝一个新值。最好是没有人告诉你它是一个动态属性——当你第一次请求它后,它会保存下来,故要尽可能地推迟资源分配。 
            <BR><BR>5.4 索引<BR>你想过象访问数组那样使用索引访问类吗 ?使用C#的索引功能,对它的期待便可了结。 
            <BR><BR>语法基本上象这样:<BR>属性 修饰符 声明 { 声明内容} <BR><BR>具体的例子为<BR>public 
            string this[int nIndex]<BR>{<BR>get { ... }<BR>set { ... }<BR>} 
            <BR><BR>索引返回或按给出的index设置字符串。它没有属性,但使用了public修饰符。声明部分由类型string和this 
            组成用于表示类的索引。get和set的执行规则和属性的规则相同。(你不能取消其中一个。) 

⌨️ 快捷键说明

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