21天学通C#阅读笔记(二)
2008-10-29 09:33
387 查看
第六天 类
---------------------------------------------------------------------
面向对象涉及的概念
封装:
封装指的是建立类(包),其中包含所需的所有东西.
建立一个类,其中存储了所需要的变量,以及操纵数据的程序.用户无需关心程序的工作原理,只要知道如何与其交互.得到所需结果.
多态:
多态指能呈现多种形式的功能,即程序能够使用您发送给它的东西进行工作.
如WriteLine(),可以打印任何类型的变量或字符串.能够接受大部分数据类型,因此是多态的.
继承:
继承是在一个类(对象)是另一个类的扩展时发生.
11天的课程中,将详细介绍如何将继承用于类和编程中.
重用:
创建类后,可以重复使用它来创建大量的对象.
通过使用继承以及其他特性,可以创建出能够在许多程序中以许多不同方式重复使用的例程.
通过封装,可以创建出经过测试并被证明能够正确运行的例程.而用户无需详细测试功能,只需正确的使用即可.
这都使得程序开发过程简单而快速.
-------------------------------------------------------
对象和类
类是创建对象的定义,而对象则是实际被创建出来的.(太难理解的感念性东西了.接下来三天都是要介绍类的知识)
定义类:
关键字class被用来定义类,格式如下
class ClassName
{
ClassBody;
}
ClassName是给类指定名称,ClassBody是组成类的代码.
类名和变量名一样,可以任意指定,习惯上将类名和另存的文件名统一.
Microsoft.NeT框架中包含大量内置类.例如:Console类
这个类的名称为Console,其中包含了Write和WriteLine程序的代码.
--------------------------------------------------------------------
声明类:
定义类后,可以用类来创建对象.类只是一种用于创建对象的定义.
类本身并不能存储信息或执行例程,只是声明对象.
对象可以按照类定义的方式来存储信息和执行例程.
注:声明对象常常被称为实例化.或者说,对象是类的一个实例.
格式如下
ClassName ObjectIdentifier = new ClassName();
ClassName是类的名称,ObjectIdentifier是要声明对象的名称.
这里涉及到一个新的关键字new.使用关键字new来创建一个对象.
new ClassName()创建一个对象,new指示为这个新对象预留内存空间.
声明对象时,必须在赋值运算符右边的类名后加上圆括号,使用类来构造一个新对象.
和所有语句一样,声明结束时,以分号指示语句结束.
----------------------------------------------------------------
类成员
类中可以包含:数据成员和函数成员.
数据成员包括变量和常量.
函数成员指的是执行操作的程序.(第七天介绍创建和使用自己的成员函数)
数据成员:
通过实例来理解
class point
{
int x;
int y;
}
假设point类用来存储点的x坐标和y坐标.
现在需要加入访问限定符public
class point
{
public int x;
public int y;
}
不加public,将无法在point外面访问x和y.
除非特别说明,否则只能在声明变量的代码块内访问该变量
访问数据成员
声明数据成员后,public使得程序可以在类的外面访问数据成员.
如在同一个程序中,声明了point的两个对象StartPoint和EndingPoint
point StartingPoint = new point();
point EndingPoint = new point();
现在使用x和y,需要确定访问的是那个坐标.
使用StartingPoint.x
StartingPoint.y
来访问StartingPoint的坐标.
使用EndingPoint.x
EndingPoint.y
来访问EndingPoint的坐标.
老办法,通过程序来理解声明对象
//point.cs - A class with two data members
//----------------------------------------
class point
{
public int x;
public int y;
}
class PointApp
{
public static void Main()
{
point starting = new point();
point ending = new point();
starting.x = 1;
starting.y = 4;
ending.x = 10;
ending.y = 11;
System.Console.WriteLine("point 1:{0}.{1}", starting.x, starting.y);
System.Console.WriteLine("point 2:{0},{1}",ending.x,ending.y);
}
}
程序演示了如何给数据成员赋值以及如何获取数据成员的值.
使用数据成员
类的数据成员和其他任何类型的变量一样.可以用于运算,控制语句等
上程序
//line.cs - Calculate the length of a line.
//-----------------------------------------
class point
{
public int x;
public int y;
}
class LineApp
{
public static void Main()
{
point starting = new point();
point ending = new point();
starting.x = 1;
starting.y = 4;
ending.x = 10;
ending.y = 11;
double line;
line = System.Math.Sqrt((ending.x - starting.x) * (ending.x - starting.x) + (ending.y - starting.y) * (ending.y - starting.y));
System.Console.WriteLine("Point 1:{0},{1}",starting.x,starting.y);
System.Console.WriteLine("Point 2:{0},{1}", ending.x, ending.y);
System.Console.WriteLine("Length of the line from point 1 to point 2:{0}",line);
}
}
程序建立了两个点,确定两点的坐标,通过一个数学计算类System.Math.Sqrt来计算出两点之间直线的长度.
在计算中,数据成员的使用方式与其他变量相同,但命名模式不一样.
将类用作数据成员
一个类中可以嵌套另一个类.类也是一种数据类型,使用类声明的对象只不过是一种更复杂的变量而已.
下面程序中包含一个line类,类成员由两个point对象(starting和ending)组成.
//line2.cs - A class with two data members.
//-----------------------------------------
class point
{
public int x;
public int y;
}
class line
{
public point starting = new point();
public point ending = new point();
}
class LineApp
{
public static void Main()
{
line MyLine = new line();
MyLine.starting.x = 1;
MyLine.starting.y = 4;
MyLine.ending.x = 10;
MyLine.ending.y = 11;
System.Console.WriteLine("point 1:{0},{1}", MyLine.starting.x, MyLine.starting.y);
System.Console.WriteLine("point 2:{0},{1}", MyLine.ending.x, MyLine.ending.y);
}
}
其中:MyLine.starting.x=1;实际上是将MyLine的starting成员的x成员赋值为1.
图6.2很直观的说明了MyLine类的成员及其名称
将上面的代码段进行改造
class line
{
public class point
{
public int x;
public int y;
}
public point starting = new point();
public point ending = new point();
}
将point类嵌套在line类里.
在这里point类只能在line类中使用,且point类也必须public定义为公有.
静态变量
有时候,希望使用同一个类声明的对象可以共享同一个值.
使用static限定符可以实现该期望.
//StatLine.cs - A class with two data members
//--------------------------------------------
class point
{
public int x;
public int y;
}
class line
{
static public point origin = new point();
public point ending = new point();
}
class LineApp
{
public static void Main()
{
line line1 = new line();
line line2 = new line();
//line的起点
line.origin.x = 1;
line.origin.y = 2;
//line1的结束点
line1.ending.x = 3;
line1.ending.y = 4;
//line2的结束点
line2.ending.x = 7;
line2.ending.y = 8;
//输出line 1和line 2的位置
System.Console.WriteLine("line 1 的起点:{0},{1}",line.origin.x,line.origin.y);
System.Console.WriteLine("line 1 的结束点:{0},{1}", line1.ending.x, line1.ending.y);
System.Console.WriteLine("line 2 的起点:{0},{1}",line.origin.x,line.origin.y);
System.Console.WriteLine("line 2 的结束点:{0},{1}", line2.ending.x, line2.ending.y);
//修改line 2的起点
line.origin.x = 939;
line.origin.y = 747;
System.Console.WriteLin
11b2c
e("--------------------------------------------------------");
//重新输出line 1和line 2的位置
System.Console.WriteLine("line 1 的起点:{0},{1}", line.origin.x, line.origin.y);
System.Console.WriteLine("line 1 的结束点:{0},{1}", line1.ending.x, line1.ending.y);
System.Console.WriteLine("line 2 的起点:{0},{1}", line.origin.x, line.origin.y);
System.Console.WriteLine("line 2 的结束点:{0},{1}", line2.ending.x, line2.ending.y);
}
}
程序中,设置了line 1 和line 2 两条线的共用起点和各自的结束点.
注:如果访问静态数据成员时使用对象名(如上程序中:line1),则出错.访问静态数据成员时,必须使用类名.
经测试分析.访问静态数据成员必须使用类名.以上程序为例:line.origin.x=1;正确,line1.origin.x=1;错误;
访问非静态数据成员必须使用对象名.以上程序为例:line.ending.x=1;错误,line1.ending.x=1;正确.
由于origin对象被声明为静态的(static),因此它将在所有的line对象之间共享.
静态数据成员最常见用途是用作计数器.当某个对象执行某种操作时,可以将所有对象的计数器加1.
-------------------------------------------------------------------------------------------
应用程序类,在第七天的课程中将详细介绍.
---------------------------------------------------
属性
面向对象的优点之一是能够控制数据的内部表示以及对数据的访问.
之前的范例,所有数据成员都被声明为公有的,所以可以在任何代码中访问这些数据成员.
但是,在面向对象的程序中,希望对哪些人可以访问(不可以访问)数据有更大的控制权.
通常不希望在类的外面可以直接访问数据成员,如果允许这样做,将陷入无法改变数据类型的境地.
c#中提供了一种属性(properties)的概念,使得能够在类中创建面向对象的字段(field).
属性使用关键字get和set来获取和设置变量的值.
概念性的东西太难理解了,还是上程序吧
//prop.cs - Using Properties
//--------------------------
class point
{
int my_x; //my_x is private
int my_y; //my_y is private
public int x
{
get
{
return my_x;
}
set
{
my_x = value;
}
}
public int y
{
get
{
return my_y;
}
set
{
my_y = value;
}
}
}
class MyApp
{
public static void Main()
{
point starting = new point();
point ending = new point();
starting.x = 1;
starting.y = 4;
ending.x = 10;
ending.y = 11;
System.Console.WriteLine("point 1:{0},{1}", starting.x, starting.y);
System.Console.WriteLine("point 2:{0},{1}", ending.x, ending.y);
}
}
程序看起来很复杂,分析下,其实实现功能很简单.
由于创建了两个my_x和my_y浪个数据成员,并没有声明他们是公有的.因此在类外面是无法访问到他们的.
所以被视为私有(private).
使用get和set来为my_x和my_y创建属性.
在程序中,int x后并没有结束符分号,所以声明该变量还包括类似
get
{
return my_x;
}
set
{
my_x = value;
}
这样的代码.
当执行x=10;的赋值程序完成后,通过set属性将10传递给my_x
当程序要取得x的值时,get属性将被调用,通过return my_x;将值传递给x.
在这里,着重说明下,get和set中,可以包含任何代码.也可以执行任何操作.甚至不一定非要将值写入令一个数据成员.
---------------------------------------------------------------------------------------------------------
名称空间
开始学习类后,必须知道存在大量可以完成各种功能的类. .net框架提供了大量的基类提供使用,另外还可以获得第三方的类.
第16天将专门介绍如何使用重要的.net基类
System.Console.Write("Hi Justin");
"Hi Justin"是一个字面值,Console是一个基类一个使用类声明的对象,Write是Console的一个例程.
而System
由于类的数量非常大,因此以一定的方式对其进行组织至关重要.可以将类集中在一起,形成名称空间.
名称空间就是一组被命名的类.Console类属于System名称空间.
System.Console.Write是一个全限定名称.使用全限定名称可以直接指定代码所在的位置.
C#中使用using来实现类和方法的简捷方式,无需制定全限定名称.
using
关键字using使得能够在程序中包含名称空间,这样使程序知道到哪个名称空间区搜索其中使用的类和例程.
格式: using namespace_name (放置在程序中的开始位置.java中是 import namespace_name)
namespace_name是名称空间或嵌套名称空间的名称.
//namesp.cs - Namespaces and the using keyword
//--------------------------------------------
using System;
class name
{
public string first;
public string last;
}
class NameApp
{
public static void Main()
{
name you = new name();
Console.Write("Enter your first name and press enter:");
you.first = Console.ReadLine();
System.Console.Write("/n{0},enter your last name and press enter:", you.first);
you.last = Console.ReadLine();
Console.WriteLine("/nData has been entered....");
System.Console.Write("You claim to be: {0} {1}", you.first, you.last);
}
}
程序很容易理解.只是其中使用Console的另一个例程ReadLine.
ReadLine用来读取用户在按Enter之前输入的信息.
嵌套名称空间
理解一下概念,很容易懂得如何使用.
如使用System名称空间的Data名称空间,可以
using System.Data;
--------------------------------------------------------------------------------------
第六天,如何定义类的基本结构.创建类的数据成员,使用存取器set和get,名称空间和using语句
C#的关键所在.并未碰到不能理解的问题.需要多次温习和写程序来巩固这一章节
=======================================================================================
第七天
类方法和成员函数
.创建自己的方法;
.使用参数将信息传递给例程;
.值和引用的概念;
.调用方法的概念;
.构造函数;
.释放类;
--------------------------------------
在c#中,例程被成为函数或方法,这两个术语都可以使用.
c++和c#开发人员习惯将例程成为方法,而多数c程序员习惯称其为函数.
定义:方法是一个带名称的,独立的,以可重用的方式放置的代码片段.
方法无需应用程序其他部分的干预就能运行,如果创建的正确,则能够执行其名称指示的特定任务.
使用方法
之前的程序中使用过几个方法:Write,WriteLine,ReadLine.再每个程序中,都使用了Main方法.
通过一个类来了解如何使用方法:
// circle.cs - A simple circle class with Methods
//-----------------------------------------------
class Circle
{
public int x;
public int y;
public double radius;
public double area()
{
double theArea;
theArea = 3.14159 * radius * radius;
return theArea;
}
public double circumference()
{
double theCirc;
theCirc = 2 * 3.14159 * radius;
return theCirc;
}
}
class CircleApp
{
public static void Main()
{
Circle first = new Circle();
Circle second = new Circle();
double area;
double circ;
first.x = 10;
first.y = 14;
first.radius = 3;
second.x = 10;
second.y = 11;
second.radius = 4;
System.Console.WriteLine("Circle 1: Center={0},{1}", first.x, first.y);
System.Console.WriteLine("Radius={0}", first.radius);
System.Console.WriteLine("Area={0}", first.area());
System.Console.WriteLine("circum={0}", first.circumference());
area = second.area();
circ = second.circumference();
System.Console.WriteLine("Circle 2: Center={0},{1}",second.x,second.y);
System.Console.WriteLine("Radius={0}", second.radius);
System.Console.WriteLine("Area={0}", area);
System.Console.WriteLine("Circum={0}", circ);
}
}
程序中,10~15行定义了一个成员方法area.该方法计算圆的面积并将其返回给调用程序.
从class CircleApp开始是程序的应用程序类CircleApp.
应用程序类中包含Main方法,每个应用程序的入口都是Main方法.
程序中创建了两个Circle对象first和second,然后给其数据成员赋值.
然后将定义的两个圆的数据成员输出.区别是第二个圆使用了变量area来获取area方法的结果.
在调用成员方法area时,由于它不是数据成员,所以后面要加圆括号.
接下来详细介绍如何定义自己的方法以及方法的工作方式.
-----------------------------------------------------------------
方法是一个独立的代码片段,被封装和命名.
程序中方法被调用时,程序流程将切换到方法,执行方法的代码,然后返回.
格式:
Method header
{
Method body
}
方法头-Method header定义了关于方法的几项内容:
.对方法的访问权限;
.返回的数据类型;
.传递给方法的值;
.方法的名称;
在上面的程序中可以知道,area方法的头如下:
public double area()
该方法被声明为公有的-public,即可以被类外面的程序访问.
返回的数据类型为double.即给调用程序返回一个double类型的值.
方法名称为area.
由于圆括号内为空,因此不用给area方法传递任何值,它使用同一个类的数据成员的值.
注:方法头不以分号结束,方法头中()内为空的话,将使用同一个类的数据成员的值.
方法的返回数据类型
在方法头中指定方法的返回数据类型.在这里可以是任何合法的数据类型.
声明返回数据类型后,必须将一个返回数据类型的值返回给调用该方法的程序.
使用关键字return,后面跟一个数据类型为方头中指定的值或变量.
如果方法不需要返回值,此时就不需要声明返回数据类型,而使用关键字void.
void指示方法不返回值
给方法命名
一种流行的准则是,结合使用一个动词和一个名词,因为方法执行某种操作,因此总是能组合使用一个动词和一个名词
例如上面程序中area的命名就不太明确含义.而GetArea是更好的选择.
方法体
方法体包含方法被调用时所执行的代码.被包含在{}中.
通常方法体中只修改其所属类的数据成员或传递给方法的参数.
方法体中返回的变量的数据类型必须与方法头中指定的数据类型相同.
使用方法
调用方法与调用数据成员相同.
对象名.方法名() 如上面程序里的:first.area()
在方法中使用数据成员.
以前在使用数据成员时,需要包含对象名.而在类的成员方法中使用数据成员,可以省略对象名.
在类的成员方法中声明变量,这些变量只在方法运行时有效,他们被成为方法的局部变量.成员方法运行结束后,变量中的值以及变量自身都将不存在.
通过程序来了解局部变量的用法和程序流程.
//locals.cs - Local variables
//---------------------------
using System;
class loco
{
public int x;
public void count_x()
{
int x;
Console.WriteLine("In count_x method. Printing X values....");
for (x = 0; x <= 10; x++)
{
Console.Write("{0}-", x);
}
Console.WriteLine("/nAt the end of count_x method.x={0}", x);
}
}
class CircleApp
{
public static void Main()
{
loco locals = new loco();
int x = 999;
locals.x = 555;
Console.WriteLine("/nIn Main(),x={0}",x);
Console.WriteLine("locals.x={0}", locals.x);
Console.WriteLine("Calling Method");
locals.count_x();
Console.WriteLine("/nBack From Method");
Console.WriteLine("Locals.x={0}", locals.x);
Console.WriteLine("In Main(),x={0}", x);
}
}
从程序中可以了解,定义了loco的数据成员int x,count_x方法的局部变量int x,Main方法的变量int x.
这三个变量虽然名称相同,从程序的输出结果中可以看到,他们之间是互不影响的.
如果要在count_x方法中使用类数据成员x.但在类的方法中使用类数据成员,无需制定对象名,也无法指定.因为对象名是不确定的.
那么这种情况下,方法中既包含局部变量,又如何使用类数据成员x呢,看程序:
//locals.cs - local variables
//------------------------------
using System;
class loco
{
public int x;
public void count_x()
{
int x;
Console.WriteLine("In count_x metnod. Printing X values");
for (x = 0; x <= 10; x++)
{
Console.Write("{0}-", x);
}
//与上一程序的区别
Console.WriteLine("/nDone looping. x={0}", x);
Console.WriteLine("the data member x`s value:{0}", this.x);
Console.WriteLine("At the end of count_x method");
}
}
class CircleApp
{
public static void Main()
{
loco locals = new loco();
int x = 999;
locals.x = 555;
Console.WriteLine("/nIn Main(),x={0}", x);
Console.WriteLine("locals.x={0}", locals.x);
Console.WriteLine("Calling Method");
locals.count_x();
Console.WriteLine("/nBack From Method");
Console.WriteLine("Locals.x={0}", locals.x);
Console.WriteLine("In Main(),x={0}", x);
}
}
在这里,使用了关键字this.关键字this指的是当前使用的对象.
在本程序中,this指的是count_x方法被调用的locals对象.由于this指的是当前对象,因此
this.x中的x指的是数据成员x,而不是局部变量x.
----------------------------------------------------------------------------------
给方法传递值
方法被调用时,可以接受值.为此,必须在方法头中定义参数.格式如下:
Modifiers ReturnType Name(Parameters)
参数是在括号中传递的,如没有参数就像前面的程序,将括号留空.传递参数的基本格式如下:
[attribute] Type ArgumentName
Type是传递的参数的数据类型,ArgumentName是要传递变量的名称.还可以指定属性(attribute,可选).
看程序:接收两个数字,将其相乘,然后将结果返回.
// mult - Passing values
//----------------------
using System;
class Multiply
{
static long multi(long nbr1, long nbr2)
{
return (nbr1 * nbr2);
}
public static void Main()
{
long x = 1234;
long y = 5678;
long a = 6789;
long b = 9876;
long result;
result = multi(x, y);
Console.WriteLine("x*y:{0}*{1}={2}", x, y, result);
result = multi(a, b);
Console.WriteLine("a*b:{0}*{1}={2}", a, b, result);
result = multi(555L, 1000L);
Console.WriteLine("with long values passed,the result is {0}", result);
}
}
方法名multi,接受两个long型参数nbr1,nbr2,他们是方法的局部变量.
给方法传参的过程使用了不同的方式,可以传递变量,也可以传递字面值.
传参的时候,参数数目必须与定义的相同.否则会出错.
针对使用过c++的程序员.需要注意,c#中的参数没有默认值.
命名方法的时候,使用了static限定符.在本程序中,multi方法和Main方法位于同一个类中,因此将它声明为
静态的.这样使得multiply类中任何地方都可以使用multi方法.如果将multi方法放在另一个类中,就需要声
该类的一个对象,然后使用对象名调用方法.
---------------------------------------------------------------------------------------------------
不同参数的访问属性
参数的访问属性有三类
.值
.引用
.输出
以值的方式访问参数
从前面的例程中了解到,以值的方式访问参数时,参数的一个拷贝将被传递给方法,方法使用这些拷贝.而原来的值不受影响.
以引用的方式访问参数
有时候,需要改变原来变量的值.在这种情况下,传递的是变量,而不是变量的值.
修改引用,将修改原来变量的值.
从技术上说,引用变量指向内存中的位置.其中存储了数据,修改该位置,所以原变量的值也随之修改.
声明参数时,默认情况下,其属性为数据类型的属性.对于基本数据类型.为按值.
要使基本数据类型,如int,按引用传递.需要在方法头中的数据类型前面加上关键字ref.
通过程序来了解如何使用关键字ref,以及值参数和引用参数之间的区别.
//refnot.cs - without reference variables (by value)
//按值传递
//--------------------------------------------------
using System;
class nbr
{
public double square(double x)
{
x = x * x;
return x;
}
}
class TestApp
{
public static void Main()
{
nbr doit = new nbr();
double nbr1 = 3;
double retval = 0;
Console.WriteLine("Before square ->nbr1={0},retval={1}",nbr1=3,retval);
retval=doit.square(nbr1);
Console.WriteLine("After square ->nbr1={0},retval={1}",nbr1,retval);
}
}
//refnot.cs - reference variables
//按引用传递
//--------------------------------------------------
using System;
class nbr
{
public double square(ref double x)
{
x = x * x;
return x;
}
}
class TestApp
{
public static void Main()
{
nbr doit = new nbr();
double nbr1 = 3;
double retval = 0;
Console.WriteLine("Before square ->nbr1={0},retval={1}",nbr1=3,retval);
retval=doit.square( ref nbr1);
Console.WriteLine("After square ->nbr1={0},retval={1}",nbr1,retval);
}
}
通过对比两个程序发现,使用按引用传递,在方法调用后,给方法传递的变量的值也被修改.
使用按引用传递格式:在方法头中声明参数时,加上关键字ref.(在给方法传参的时候,同样也要加ref)
以输出方式访问参数
通过制定返回类型,可以从方法返回一个值.使用引用变量,可以返回多个值.
在c#中,海提供了一种特殊的属性类型,专门用于从方法返回数据.
在发放头中加入专门用于返回值的参数.声明方式与其他参数相同.只是需要将其属性设置为关键字out.
该关键字指示从方法返回一个值,但不需要传入.调用包含输出参数的方法时,一定要包含用于存储返回值的变量.
程序来了
// ourrer.cs - Using output variables
//-----------------------------------
using System;
class nbr
{
public void math_routines(double x, out double half, out double squared, out double cubed)
{
half = x / 2;
squared = x * x;
cubed = x * x * x;
}
}
class TestApp
{
public static void Main()
{
nbr doit = new nbr();
double nbr = 600;
double HalfNbr = 0;
double SquaredNbr = 0;
double CubedNbr = 0;
Console.WriteLine("Before Method ->nbr={0}", nbr);
Console.WriteLine("HalfNbr={0}", HalfNbr);
Console.WriteLine("SquaredNbr={0}", SquaredNbr);
Console.WriteLine("CubedNbr={0}/n", CubedNbr);
doit.math_routines(nbr, out HalfNbr, out SquaredNbr, out CubedNbr);
Console.WriteLine("After Medthod ->nbr={0}", nbr);
Console.WriteLine("HalfNbr={0}", HalfNbr);
Console.WriteLine("SquaredNbr={0}", SquaredNbr);
Console.WriteLine("CubedNbr={0}", CubedNbr);
}
}
调用方法时,传递参数的属性也必须指定为out.
方法中,必须给输出参数设置值.(并不会像有些程序,不指定值,默认NULL)
否则就会出错.
------------------------------------------------------------------------------------
类方法的类型
.属性存取器方法;
.构造函数;
.析构函数;
-------------------------------------------------------------------------------------
属性存取器方法
前面已经使用过属性存取器方法,通过get和set使得类数据成员成为私有的
构造函数
有时候,在对象被创建时,您想做一些设置.有一种特殊的方法专门用于完成对象初始设置或构造.这种方法就叫做构造函数.
实际上存在两种构造函数:实例构造函数和静态构造函数,前者在每个实例或对象创建时调用,后者在类的第一个对象被创建之前被调用.
a.实例构造函数
实例构造函数是一个方法,每当实例化一个对象时,该方法都被调用.
这种构造函数可以包含常规函数能包含的任何代码.
通常用于完成对象的初始化设置,可以包含诸如初始化变量等功能.
格式如下:
modifiers classname()
{
//Constructor body
}
构造函数的名称与其所属的类相同,其中限定符与其他方法的限定符相同.通常只使用public,不指定返回数据类型.
注:每个类都有一个默认的构造函数,即使没有创建任何构造函数.通过创建构造函数,可以控制某些设置.
当创建一个对象时,相应类的构造函数都将被调用.通过程序来了解如何使用构造函数
//constr.cs - constructors
//------------------------
using System;
public class MyClass
{
static public int sctr = 0;
public int ctr = 0;
public void routine()
{
Console.WriteLine("In the routine - ctr={0},sctr={1}/n", ctr, sctr);
}
public MyClass()
{
ctr++;
sctr++;
Console.WriteLine("In Constructor - ctr={0},sctr={1}", ctr, sctr);
}
}
class TestApp
{
public static void Main()
{
Console.WriteLine("Start of main method...");
Console.WriteLine("Creating first object...");
MyClass first = new MyClass();
Console.WriteLine("Creating second object...");
MyClass second = new MyClass();
Console.WriteLine("Calling first routine...");
first.routine();
Console.WriteLine("Creating third object...");
MyClass third = new MyClass();
Console.WriteLine("Calling third routine...");
third.routine();
Console.WriteLine("Calling second routine...");
second.routine();
Console.WriteLine("End of the Main method");
}
}
分析上面的程序.Myclass类中,初始化了两个变量ctr和sctr.
TestApp中共创建了MyClass的3个对象first,second,third.每创建一个对象,就调用一次构造函数.
由于ctr没有被声明为静态数据成员.每次构造函数运行,使用的是ctr的拷贝.
sctr被声明为静态数据成员,每次构造函数运行,sctr的值也随之修改.
需要注意到的是,调用第二个对象时,second.routine,sctr的值是3,这是因为sctr是在对象之间共享的,当打印时,已经调用了三次构造函数.
构造函数是自动调用的,在程序中不要调用构造函数.(明天将介绍如何给构造函数传自参数.)
b.静态构造函数
与数据成员和方法一样,也可以创建静态构造函数.
使用static声明的构造函数,将在第一个对象被创建之前调用,且只被调用一次,以后便不再被使用.
下面程序中使用了静态构造函数
// statcon.cs - static constructors
//---------------------------------
using System;
public class test
{
static public int sctr;
public int ctr;
public void routine()
{
Console.WriteLine("In the routine - ctr = {0},sctr = {1}/n", ctr, sctr);
}
static test()
{
sctr = 100;
Console.WriteLine("In Static Constructor - sctr = {0} /n", sctr);
}
public test()
{
ctr++;
sctr++;
Console.WriteLine("In Constructor - ctr = {0} , sctr= {1} /n", ctr, sctr);
}
}
class TestApp
{
public static void Main()
{
Console.WriteLine("Start of Main method...");
Console.WriteLine("Creating first object...");
test first = new test();
Console.WriteLine("Creating second object...");
test second = new test();
Console.WriteLine("Calling first routine...");
first.routine();
Console.WriteLine("Creating third object...");
test third = new test();
Console.WriteLine("Calling third routine...");
third.routine();
Console.WriteLine("Calling second routine...");
second.routine();
Console.WriteLine("End of Main method");
}
}
跟之前的使用构造函数没有什么不同,只是添加了一个静态构造函数.在创建第一个对象前,将sctr变量值修改为100.
c.析构函数
可以在对象被释放时执行某些操作.
当程序不再使用对象后的某一时刻,析构函数自动执行.
所谓某一时刻,并不明确.
事实上,程序结束时也可能调用不到析构函数,何时执行,并不能真正控制,所以析构函数价值有限.
--------------------------------------------------------------------------------------
第七天,好艰难的一天,学习的内容并不算了,但是需要理解.
今天写程序的过程中,发现前几天的课程有些生疏.看来在今天结束后安排一个一周复习,确实有它的道理
继续练习ing..............
--------------------------------------------------------------------------------------
课后练习中发现有个概念不是很明确,加强下:
访问类的成员方法时,使用什么的格式呢,例如使用myclass类实例化myObject对象.该类包含myMtehod方法.
a.myClass.myMethod:如果方法被声明为静态的,则正确,否则错误
b.myObject.myMethod:针对对象实例调用类方法的标准公示.如果方法被声明为静态,则错误.
c.myMethod:仅当该调用与方法位于同一个类中才正确.
=======================================================================================
---------------------------------------------------------------------
面向对象涉及的概念
封装:
封装指的是建立类(包),其中包含所需的所有东西.
建立一个类,其中存储了所需要的变量,以及操纵数据的程序.用户无需关心程序的工作原理,只要知道如何与其交互.得到所需结果.
多态:
多态指能呈现多种形式的功能,即程序能够使用您发送给它的东西进行工作.
如WriteLine(),可以打印任何类型的变量或字符串.能够接受大部分数据类型,因此是多态的.
继承:
继承是在一个类(对象)是另一个类的扩展时发生.
11天的课程中,将详细介绍如何将继承用于类和编程中.
重用:
创建类后,可以重复使用它来创建大量的对象.
通过使用继承以及其他特性,可以创建出能够在许多程序中以许多不同方式重复使用的例程.
通过封装,可以创建出经过测试并被证明能够正确运行的例程.而用户无需详细测试功能,只需正确的使用即可.
这都使得程序开发过程简单而快速.
-------------------------------------------------------
对象和类
类是创建对象的定义,而对象则是实际被创建出来的.(太难理解的感念性东西了.接下来三天都是要介绍类的知识)
定义类:
关键字class被用来定义类,格式如下
class ClassName
{
ClassBody;
}
ClassName是给类指定名称,ClassBody是组成类的代码.
类名和变量名一样,可以任意指定,习惯上将类名和另存的文件名统一.
Microsoft.NeT框架中包含大量内置类.例如:Console类
这个类的名称为Console,其中包含了Write和WriteLine程序的代码.
--------------------------------------------------------------------
声明类:
定义类后,可以用类来创建对象.类只是一种用于创建对象的定义.
类本身并不能存储信息或执行例程,只是声明对象.
对象可以按照类定义的方式来存储信息和执行例程.
注:声明对象常常被称为实例化.或者说,对象是类的一个实例.
格式如下
ClassName ObjectIdentifier = new ClassName();
ClassName是类的名称,ObjectIdentifier是要声明对象的名称.
这里涉及到一个新的关键字new.使用关键字new来创建一个对象.
new ClassName()创建一个对象,new指示为这个新对象预留内存空间.
声明对象时,必须在赋值运算符右边的类名后加上圆括号,使用类来构造一个新对象.
和所有语句一样,声明结束时,以分号指示语句结束.
----------------------------------------------------------------
类成员
类中可以包含:数据成员和函数成员.
数据成员包括变量和常量.
函数成员指的是执行操作的程序.(第七天介绍创建和使用自己的成员函数)
数据成员:
通过实例来理解
class point
{
int x;
int y;
}
假设point类用来存储点的x坐标和y坐标.
现在需要加入访问限定符public
class point
{
public int x;
public int y;
}
不加public,将无法在point外面访问x和y.
除非特别说明,否则只能在声明变量的代码块内访问该变量
访问数据成员
声明数据成员后,public使得程序可以在类的外面访问数据成员.
如在同一个程序中,声明了point的两个对象StartPoint和EndingPoint
point StartingPoint = new point();
point EndingPoint = new point();
现在使用x和y,需要确定访问的是那个坐标.
使用StartingPoint.x
StartingPoint.y
来访问StartingPoint的坐标.
使用EndingPoint.x
EndingPoint.y
来访问EndingPoint的坐标.
老办法,通过程序来理解声明对象
//point.cs - A class with two data members
//----------------------------------------
class point
{
public int x;
public int y;
}
class PointApp
{
public static void Main()
{
point starting = new point();
point ending = new point();
starting.x = 1;
starting.y = 4;
ending.x = 10;
ending.y = 11;
System.Console.WriteLine("point 1:{0}.{1}", starting.x, starting.y);
System.Console.WriteLine("point 2:{0},{1}",ending.x,ending.y);
}
}
程序演示了如何给数据成员赋值以及如何获取数据成员的值.
使用数据成员
类的数据成员和其他任何类型的变量一样.可以用于运算,控制语句等
上程序
//line.cs - Calculate the length of a line.
//-----------------------------------------
class point
{
public int x;
public int y;
}
class LineApp
{
public static void Main()
{
point starting = new point();
point ending = new point();
starting.x = 1;
starting.y = 4;
ending.x = 10;
ending.y = 11;
double line;
line = System.Math.Sqrt((ending.x - starting.x) * (ending.x - starting.x) + (ending.y - starting.y) * (ending.y - starting.y));
System.Console.WriteLine("Point 1:{0},{1}",starting.x,starting.y);
System.Console.WriteLine("Point 2:{0},{1}", ending.x, ending.y);
System.Console.WriteLine("Length of the line from point 1 to point 2:{0}",line);
}
}
程序建立了两个点,确定两点的坐标,通过一个数学计算类System.Math.Sqrt来计算出两点之间直线的长度.
在计算中,数据成员的使用方式与其他变量相同,但命名模式不一样.
将类用作数据成员
一个类中可以嵌套另一个类.类也是一种数据类型,使用类声明的对象只不过是一种更复杂的变量而已.
下面程序中包含一个line类,类成员由两个point对象(starting和ending)组成.
//line2.cs - A class with two data members.
//-----------------------------------------
class point
{
public int x;
public int y;
}
class line
{
public point starting = new point();
public point ending = new point();
}
class LineApp
{
public static void Main()
{
line MyLine = new line();
MyLine.starting.x = 1;
MyLine.starting.y = 4;
MyLine.ending.x = 10;
MyLine.ending.y = 11;
System.Console.WriteLine("point 1:{0},{1}", MyLine.starting.x, MyLine.starting.y);
System.Console.WriteLine("point 2:{0},{1}", MyLine.ending.x, MyLine.ending.y);
}
}
其中:MyLine.starting.x=1;实际上是将MyLine的starting成员的x成员赋值为1.
图6.2很直观的说明了MyLine类的成员及其名称
将上面的代码段进行改造
class line
{
public class point
{
public int x;
public int y;
}
public point starting = new point();
public point ending = new point();
}
将point类嵌套在line类里.
在这里point类只能在line类中使用,且point类也必须public定义为公有.
静态变量
有时候,希望使用同一个类声明的对象可以共享同一个值.
使用static限定符可以实现该期望.
//StatLine.cs - A class with two data members
//--------------------------------------------
class point
{
public int x;
public int y;
}
class line
{
static public point origin = new point();
public point ending = new point();
}
class LineApp
{
public static void Main()
{
line line1 = new line();
line line2 = new line();
//line的起点
line.origin.x = 1;
line.origin.y = 2;
//line1的结束点
line1.ending.x = 3;
line1.ending.y = 4;
//line2的结束点
line2.ending.x = 7;
line2.ending.y = 8;
//输出line 1和line 2的位置
System.Console.WriteLine("line 1 的起点:{0},{1}",line.origin.x,line.origin.y);
System.Console.WriteLine("line 1 的结束点:{0},{1}", line1.ending.x, line1.ending.y);
System.Console.WriteLine("line 2 的起点:{0},{1}",line.origin.x,line.origin.y);
System.Console.WriteLine("line 2 的结束点:{0},{1}", line2.ending.x, line2.ending.y);
//修改line 2的起点
line.origin.x = 939;
line.origin.y = 747;
System.Console.WriteLin
11b2c
e("--------------------------------------------------------");
//重新输出line 1和line 2的位置
System.Console.WriteLine("line 1 的起点:{0},{1}", line.origin.x, line.origin.y);
System.Console.WriteLine("line 1 的结束点:{0},{1}", line1.ending.x, line1.ending.y);
System.Console.WriteLine("line 2 的起点:{0},{1}", line.origin.x, line.origin.y);
System.Console.WriteLine("line 2 的结束点:{0},{1}", line2.ending.x, line2.ending.y);
}
}
程序中,设置了line 1 和line 2 两条线的共用起点和各自的结束点.
注:如果访问静态数据成员时使用对象名(如上程序中:line1),则出错.访问静态数据成员时,必须使用类名.
经测试分析.访问静态数据成员必须使用类名.以上程序为例:line.origin.x=1;正确,line1.origin.x=1;错误;
访问非静态数据成员必须使用对象名.以上程序为例:line.ending.x=1;错误,line1.ending.x=1;正确.
由于origin对象被声明为静态的(static),因此它将在所有的line对象之间共享.
静态数据成员最常见用途是用作计数器.当某个对象执行某种操作时,可以将所有对象的计数器加1.
-------------------------------------------------------------------------------------------
应用程序类,在第七天的课程中将详细介绍.
---------------------------------------------------
属性
面向对象的优点之一是能够控制数据的内部表示以及对数据的访问.
之前的范例,所有数据成员都被声明为公有的,所以可以在任何代码中访问这些数据成员.
但是,在面向对象的程序中,希望对哪些人可以访问(不可以访问)数据有更大的控制权.
通常不希望在类的外面可以直接访问数据成员,如果允许这样做,将陷入无法改变数据类型的境地.
c#中提供了一种属性(properties)的概念,使得能够在类中创建面向对象的字段(field).
属性使用关键字get和set来获取和设置变量的值.
概念性的东西太难理解了,还是上程序吧
//prop.cs - Using Properties
//--------------------------
class point
{
int my_x; //my_x is private
int my_y; //my_y is private
public int x
{
get
{
return my_x;
}
set
{
my_x = value;
}
}
public int y
{
get
{
return my_y;
}
set
{
my_y = value;
}
}
}
class MyApp
{
public static void Main()
{
point starting = new point();
point ending = new point();
starting.x = 1;
starting.y = 4;
ending.x = 10;
ending.y = 11;
System.Console.WriteLine("point 1:{0},{1}", starting.x, starting.y);
System.Console.WriteLine("point 2:{0},{1}", ending.x, ending.y);
}
}
程序看起来很复杂,分析下,其实实现功能很简单.
由于创建了两个my_x和my_y浪个数据成员,并没有声明他们是公有的.因此在类外面是无法访问到他们的.
所以被视为私有(private).
使用get和set来为my_x和my_y创建属性.
在程序中,int x后并没有结束符分号,所以声明该变量还包括类似
get
{
return my_x;
}
set
{
my_x = value;
}
这样的代码.
当执行x=10;的赋值程序完成后,通过set属性将10传递给my_x
当程序要取得x的值时,get属性将被调用,通过return my_x;将值传递给x.
在这里,着重说明下,get和set中,可以包含任何代码.也可以执行任何操作.甚至不一定非要将值写入令一个数据成员.
---------------------------------------------------------------------------------------------------------
名称空间
开始学习类后,必须知道存在大量可以完成各种功能的类. .net框架提供了大量的基类提供使用,另外还可以获得第三方的类.
第16天将专门介绍如何使用重要的.net基类
System.Console.Write("Hi Justin");
"Hi Justin"是一个字面值,Console是一个基类一个使用类声明的对象,Write是Console的一个例程.
而System
由于类的数量非常大,因此以一定的方式对其进行组织至关重要.可以将类集中在一起,形成名称空间.
名称空间就是一组被命名的类.Console类属于System名称空间.
System.Console.Write是一个全限定名称.使用全限定名称可以直接指定代码所在的位置.
C#中使用using来实现类和方法的简捷方式,无需制定全限定名称.
using
关键字using使得能够在程序中包含名称空间,这样使程序知道到哪个名称空间区搜索其中使用的类和例程.
格式: using namespace_name (放置在程序中的开始位置.java中是 import namespace_name)
namespace_name是名称空间或嵌套名称空间的名称.
//namesp.cs - Namespaces and the using keyword
//--------------------------------------------
using System;
class name
{
public string first;
public string last;
}
class NameApp
{
public static void Main()
{
name you = new name();
Console.Write("Enter your first name and press enter:");
you.first = Console.ReadLine();
System.Console.Write("/n{0},enter your last name and press enter:", you.first);
you.last = Console.ReadLine();
Console.WriteLine("/nData has been entered....");
System.Console.Write("You claim to be: {0} {1}", you.first, you.last);
}
}
程序很容易理解.只是其中使用Console的另一个例程ReadLine.
ReadLine用来读取用户在按Enter之前输入的信息.
嵌套名称空间
理解一下概念,很容易懂得如何使用.
如使用System名称空间的Data名称空间,可以
using System.Data;
--------------------------------------------------------------------------------------
第六天,如何定义类的基本结构.创建类的数据成员,使用存取器set和get,名称空间和using语句
C#的关键所在.并未碰到不能理解的问题.需要多次温习和写程序来巩固这一章节
=======================================================================================
第七天
类方法和成员函数
.创建自己的方法;
.使用参数将信息传递给例程;
.值和引用的概念;
.调用方法的概念;
.构造函数;
.释放类;
--------------------------------------
在c#中,例程被成为函数或方法,这两个术语都可以使用.
c++和c#开发人员习惯将例程成为方法,而多数c程序员习惯称其为函数.
定义:方法是一个带名称的,独立的,以可重用的方式放置的代码片段.
方法无需应用程序其他部分的干预就能运行,如果创建的正确,则能够执行其名称指示的特定任务.
使用方法
之前的程序中使用过几个方法:Write,WriteLine,ReadLine.再每个程序中,都使用了Main方法.
通过一个类来了解如何使用方法:
// circle.cs - A simple circle class with Methods
//-----------------------------------------------
class Circle
{
public int x;
public int y;
public double radius;
public double area()
{
double theArea;
theArea = 3.14159 * radius * radius;
return theArea;
}
public double circumference()
{
double theCirc;
theCirc = 2 * 3.14159 * radius;
return theCirc;
}
}
class CircleApp
{
public static void Main()
{
Circle first = new Circle();
Circle second = new Circle();
double area;
double circ;
first.x = 10;
first.y = 14;
first.radius = 3;
second.x = 10;
second.y = 11;
second.radius = 4;
System.Console.WriteLine("Circle 1: Center={0},{1}", first.x, first.y);
System.Console.WriteLine("Radius={0}", first.radius);
System.Console.WriteLine("Area={0}", first.area());
System.Console.WriteLine("circum={0}", first.circumference());
area = second.area();
circ = second.circumference();
System.Console.WriteLine("Circle 2: Center={0},{1}",second.x,second.y);
System.Console.WriteLine("Radius={0}", second.radius);
System.Console.WriteLine("Area={0}", area);
System.Console.WriteLine("Circum={0}", circ);
}
}
程序中,10~15行定义了一个成员方法area.该方法计算圆的面积并将其返回给调用程序.
从class CircleApp开始是程序的应用程序类CircleApp.
应用程序类中包含Main方法,每个应用程序的入口都是Main方法.
程序中创建了两个Circle对象first和second,然后给其数据成员赋值.
然后将定义的两个圆的数据成员输出.区别是第二个圆使用了变量area来获取area方法的结果.
在调用成员方法area时,由于它不是数据成员,所以后面要加圆括号.
接下来详细介绍如何定义自己的方法以及方法的工作方式.
-----------------------------------------------------------------
方法是一个独立的代码片段,被封装和命名.
程序中方法被调用时,程序流程将切换到方法,执行方法的代码,然后返回.
格式:
Method header
{
Method body
}
方法头-Method header定义了关于方法的几项内容:
.对方法的访问权限;
.返回的数据类型;
.传递给方法的值;
.方法的名称;
在上面的程序中可以知道,area方法的头如下:
public double area()
该方法被声明为公有的-public,即可以被类外面的程序访问.
返回的数据类型为double.即给调用程序返回一个double类型的值.
方法名称为area.
由于圆括号内为空,因此不用给area方法传递任何值,它使用同一个类的数据成员的值.
注:方法头不以分号结束,方法头中()内为空的话,将使用同一个类的数据成员的值.
方法的返回数据类型
在方法头中指定方法的返回数据类型.在这里可以是任何合法的数据类型.
声明返回数据类型后,必须将一个返回数据类型的值返回给调用该方法的程序.
使用关键字return,后面跟一个数据类型为方头中指定的值或变量.
如果方法不需要返回值,此时就不需要声明返回数据类型,而使用关键字void.
void指示方法不返回值
给方法命名
一种流行的准则是,结合使用一个动词和一个名词,因为方法执行某种操作,因此总是能组合使用一个动词和一个名词
例如上面程序中area的命名就不太明确含义.而GetArea是更好的选择.
方法体
方法体包含方法被调用时所执行的代码.被包含在{}中.
通常方法体中只修改其所属类的数据成员或传递给方法的参数.
方法体中返回的变量的数据类型必须与方法头中指定的数据类型相同.
使用方法
调用方法与调用数据成员相同.
对象名.方法名() 如上面程序里的:first.area()
在方法中使用数据成员.
以前在使用数据成员时,需要包含对象名.而在类的成员方法中使用数据成员,可以省略对象名.
在类的成员方法中声明变量,这些变量只在方法运行时有效,他们被成为方法的局部变量.成员方法运行结束后,变量中的值以及变量自身都将不存在.
通过程序来了解局部变量的用法和程序流程.
//locals.cs - Local variables
//---------------------------
using System;
class loco
{
public int x;
public void count_x()
{
int x;
Console.WriteLine("In count_x method. Printing X values....");
for (x = 0; x <= 10; x++)
{
Console.Write("{0}-", x);
}
Console.WriteLine("/nAt the end of count_x method.x={0}", x);
}
}
class CircleApp
{
public static void Main()
{
loco locals = new loco();
int x = 999;
locals.x = 555;
Console.WriteLine("/nIn Main(),x={0}",x);
Console.WriteLine("locals.x={0}", locals.x);
Console.WriteLine("Calling Method");
locals.count_x();
Console.WriteLine("/nBack From Method");
Console.WriteLine("Locals.x={0}", locals.x);
Console.WriteLine("In Main(),x={0}", x);
}
}
从程序中可以了解,定义了loco的数据成员int x,count_x方法的局部变量int x,Main方法的变量int x.
这三个变量虽然名称相同,从程序的输出结果中可以看到,他们之间是互不影响的.
如果要在count_x方法中使用类数据成员x.但在类的方法中使用类数据成员,无需制定对象名,也无法指定.因为对象名是不确定的.
那么这种情况下,方法中既包含局部变量,又如何使用类数据成员x呢,看程序:
//locals.cs - local variables
//------------------------------
using System;
class loco
{
public int x;
public void count_x()
{
int x;
Console.WriteLine("In count_x metnod. Printing X values");
for (x = 0; x <= 10; x++)
{
Console.Write("{0}-", x);
}
//与上一程序的区别
Console.WriteLine("/nDone looping. x={0}", x);
Console.WriteLine("the data member x`s value:{0}", this.x);
Console.WriteLine("At the end of count_x method");
}
}
class CircleApp
{
public static void Main()
{
loco locals = new loco();
int x = 999;
locals.x = 555;
Console.WriteLine("/nIn Main(),x={0}", x);
Console.WriteLine("locals.x={0}", locals.x);
Console.WriteLine("Calling Method");
locals.count_x();
Console.WriteLine("/nBack From Method");
Console.WriteLine("Locals.x={0}", locals.x);
Console.WriteLine("In Main(),x={0}", x);
}
}
在这里,使用了关键字this.关键字this指的是当前使用的对象.
在本程序中,this指的是count_x方法被调用的locals对象.由于this指的是当前对象,因此
this.x中的x指的是数据成员x,而不是局部变量x.
----------------------------------------------------------------------------------
给方法传递值
方法被调用时,可以接受值.为此,必须在方法头中定义参数.格式如下:
Modifiers ReturnType Name(Parameters)
参数是在括号中传递的,如没有参数就像前面的程序,将括号留空.传递参数的基本格式如下:
[attribute] Type ArgumentName
Type是传递的参数的数据类型,ArgumentName是要传递变量的名称.还可以指定属性(attribute,可选).
看程序:接收两个数字,将其相乘,然后将结果返回.
// mult - Passing values
//----------------------
using System;
class Multiply
{
static long multi(long nbr1, long nbr2)
{
return (nbr1 * nbr2);
}
public static void Main()
{
long x = 1234;
long y = 5678;
long a = 6789;
long b = 9876;
long result;
result = multi(x, y);
Console.WriteLine("x*y:{0}*{1}={2}", x, y, result);
result = multi(a, b);
Console.WriteLine("a*b:{0}*{1}={2}", a, b, result);
result = multi(555L, 1000L);
Console.WriteLine("with long values passed,the result is {0}", result);
}
}
方法名multi,接受两个long型参数nbr1,nbr2,他们是方法的局部变量.
给方法传参的过程使用了不同的方式,可以传递变量,也可以传递字面值.
传参的时候,参数数目必须与定义的相同.否则会出错.
针对使用过c++的程序员.需要注意,c#中的参数没有默认值.
命名方法的时候,使用了static限定符.在本程序中,multi方法和Main方法位于同一个类中,因此将它声明为
静态的.这样使得multiply类中任何地方都可以使用multi方法.如果将multi方法放在另一个类中,就需要声
该类的一个对象,然后使用对象名调用方法.
---------------------------------------------------------------------------------------------------
不同参数的访问属性
参数的访问属性有三类
.值
.引用
.输出
以值的方式访问参数
从前面的例程中了解到,以值的方式访问参数时,参数的一个拷贝将被传递给方法,方法使用这些拷贝.而原来的值不受影响.
以引用的方式访问参数
有时候,需要改变原来变量的值.在这种情况下,传递的是变量,而不是变量的值.
修改引用,将修改原来变量的值.
从技术上说,引用变量指向内存中的位置.其中存储了数据,修改该位置,所以原变量的值也随之修改.
声明参数时,默认情况下,其属性为数据类型的属性.对于基本数据类型.为按值.
要使基本数据类型,如int,按引用传递.需要在方法头中的数据类型前面加上关键字ref.
通过程序来了解如何使用关键字ref,以及值参数和引用参数之间的区别.
//refnot.cs - without reference variables (by value)
//按值传递
//--------------------------------------------------
using System;
class nbr
{
public double square(double x)
{
x = x * x;
return x;
}
}
class TestApp
{
public static void Main()
{
nbr doit = new nbr();
double nbr1 = 3;
double retval = 0;
Console.WriteLine("Before square ->nbr1={0},retval={1}",nbr1=3,retval);
retval=doit.square(nbr1);
Console.WriteLine("After square ->nbr1={0},retval={1}",nbr1,retval);
}
}
//refnot.cs - reference variables
//按引用传递
//--------------------------------------------------
using System;
class nbr
{
public double square(ref double x)
{
x = x * x;
return x;
}
}
class TestApp
{
public static void Main()
{
nbr doit = new nbr();
double nbr1 = 3;
double retval = 0;
Console.WriteLine("Before square ->nbr1={0},retval={1}",nbr1=3,retval);
retval=doit.square( ref nbr1);
Console.WriteLine("After square ->nbr1={0},retval={1}",nbr1,retval);
}
}
通过对比两个程序发现,使用按引用传递,在方法调用后,给方法传递的变量的值也被修改.
使用按引用传递格式:在方法头中声明参数时,加上关键字ref.(在给方法传参的时候,同样也要加ref)
以输出方式访问参数
通过制定返回类型,可以从方法返回一个值.使用引用变量,可以返回多个值.
在c#中,海提供了一种特殊的属性类型,专门用于从方法返回数据.
在发放头中加入专门用于返回值的参数.声明方式与其他参数相同.只是需要将其属性设置为关键字out.
该关键字指示从方法返回一个值,但不需要传入.调用包含输出参数的方法时,一定要包含用于存储返回值的变量.
程序来了
// ourrer.cs - Using output variables
//-----------------------------------
using System;
class nbr
{
public void math_routines(double x, out double half, out double squared, out double cubed)
{
half = x / 2;
squared = x * x;
cubed = x * x * x;
}
}
class TestApp
{
public static void Main()
{
nbr doit = new nbr();
double nbr = 600;
double HalfNbr = 0;
double SquaredNbr = 0;
double CubedNbr = 0;
Console.WriteLine("Before Method ->nbr={0}", nbr);
Console.WriteLine("HalfNbr={0}", HalfNbr);
Console.WriteLine("SquaredNbr={0}", SquaredNbr);
Console.WriteLine("CubedNbr={0}/n", CubedNbr);
doit.math_routines(nbr, out HalfNbr, out SquaredNbr, out CubedNbr);
Console.WriteLine("After Medthod ->nbr={0}", nbr);
Console.WriteLine("HalfNbr={0}", HalfNbr);
Console.WriteLine("SquaredNbr={0}", SquaredNbr);
Console.WriteLine("CubedNbr={0}", CubedNbr);
}
}
调用方法时,传递参数的属性也必须指定为out.
方法中,必须给输出参数设置值.(并不会像有些程序,不指定值,默认NULL)
否则就会出错.
------------------------------------------------------------------------------------
类方法的类型
.属性存取器方法;
.构造函数;
.析构函数;
-------------------------------------------------------------------------------------
属性存取器方法
前面已经使用过属性存取器方法,通过get和set使得类数据成员成为私有的
构造函数
有时候,在对象被创建时,您想做一些设置.有一种特殊的方法专门用于完成对象初始设置或构造.这种方法就叫做构造函数.
实际上存在两种构造函数:实例构造函数和静态构造函数,前者在每个实例或对象创建时调用,后者在类的第一个对象被创建之前被调用.
a.实例构造函数
实例构造函数是一个方法,每当实例化一个对象时,该方法都被调用.
这种构造函数可以包含常规函数能包含的任何代码.
通常用于完成对象的初始化设置,可以包含诸如初始化变量等功能.
格式如下:
modifiers classname()
{
//Constructor body
}
构造函数的名称与其所属的类相同,其中限定符与其他方法的限定符相同.通常只使用public,不指定返回数据类型.
注:每个类都有一个默认的构造函数,即使没有创建任何构造函数.通过创建构造函数,可以控制某些设置.
当创建一个对象时,相应类的构造函数都将被调用.通过程序来了解如何使用构造函数
//constr.cs - constructors
//------------------------
using System;
public class MyClass
{
static public int sctr = 0;
public int ctr = 0;
public void routine()
{
Console.WriteLine("In the routine - ctr={0},sctr={1}/n", ctr, sctr);
}
public MyClass()
{
ctr++;
sctr++;
Console.WriteLine("In Constructor - ctr={0},sctr={1}", ctr, sctr);
}
}
class TestApp
{
public static void Main()
{
Console.WriteLine("Start of main method...");
Console.WriteLine("Creating first object...");
MyClass first = new MyClass();
Console.WriteLine("Creating second object...");
MyClass second = new MyClass();
Console.WriteLine("Calling first routine...");
first.routine();
Console.WriteLine("Creating third object...");
MyClass third = new MyClass();
Console.WriteLine("Calling third routine...");
third.routine();
Console.WriteLine("Calling second routine...");
second.routine();
Console.WriteLine("End of the Main method");
}
}
分析上面的程序.Myclass类中,初始化了两个变量ctr和sctr.
TestApp中共创建了MyClass的3个对象first,second,third.每创建一个对象,就调用一次构造函数.
由于ctr没有被声明为静态数据成员.每次构造函数运行,使用的是ctr的拷贝.
sctr被声明为静态数据成员,每次构造函数运行,sctr的值也随之修改.
需要注意到的是,调用第二个对象时,second.routine,sctr的值是3,这是因为sctr是在对象之间共享的,当打印时,已经调用了三次构造函数.
构造函数是自动调用的,在程序中不要调用构造函数.(明天将介绍如何给构造函数传自参数.)
b.静态构造函数
与数据成员和方法一样,也可以创建静态构造函数.
使用static声明的构造函数,将在第一个对象被创建之前调用,且只被调用一次,以后便不再被使用.
下面程序中使用了静态构造函数
// statcon.cs - static constructors
//---------------------------------
using System;
public class test
{
static public int sctr;
public int ctr;
public void routine()
{
Console.WriteLine("In the routine - ctr = {0},sctr = {1}/n", ctr, sctr);
}
static test()
{
sctr = 100;
Console.WriteLine("In Static Constructor - sctr = {0} /n", sctr);
}
public test()
{
ctr++;
sctr++;
Console.WriteLine("In Constructor - ctr = {0} , sctr= {1} /n", ctr, sctr);
}
}
class TestApp
{
public static void Main()
{
Console.WriteLine("Start of Main method...");
Console.WriteLine("Creating first object...");
test first = new test();
Console.WriteLine("Creating second object...");
test second = new test();
Console.WriteLine("Calling first routine...");
first.routine();
Console.WriteLine("Creating third object...");
test third = new test();
Console.WriteLine("Calling third routine...");
third.routine();
Console.WriteLine("Calling second routine...");
second.routine();
Console.WriteLine("End of Main method");
}
}
跟之前的使用构造函数没有什么不同,只是添加了一个静态构造函数.在创建第一个对象前,将sctr变量值修改为100.
c.析构函数
可以在对象被释放时执行某些操作.
当程序不再使用对象后的某一时刻,析构函数自动执行.
所谓某一时刻,并不明确.
事实上,程序结束时也可能调用不到析构函数,何时执行,并不能真正控制,所以析构函数价值有限.
--------------------------------------------------------------------------------------
第七天,好艰难的一天,学习的内容并不算了,但是需要理解.
今天写程序的过程中,发现前几天的课程有些生疏.看来在今天结束后安排一个一周复习,确实有它的道理
继续练习ing..............
--------------------------------------------------------------------------------------
课后练习中发现有个概念不是很明确,加强下:
访问类的成员方法时,使用什么的格式呢,例如使用myclass类实例化myObject对象.该类包含myMtehod方法.
a.myClass.myMethod:如果方法被声明为静态的,则正确,否则错误
b.myObject.myMethod:针对对象实例调用类方法的标准公示.如果方法被声明为静态,则错误.
c.myMethod:仅当该调用与方法位于同一个类中才正确.
=======================================================================================
相关文章推荐
- 21天学通C#阅读笔记(三)
- 21天学通C#阅读笔记(四)
- 21天学通C++--阅读笔记5(输入输出、命名空间)
- 21天学通C++--阅读笔记2
- 21天学通C++--阅读笔记7(异常、宏、位操作)
- 21天学通C++--阅读笔记3
- 21天学通C++--阅读笔记1
- 21天学通C++--阅读笔记4(静态成员、函数指针)
- 21天学通C++--阅读笔记6(模板、标准模板库)
- 构建之法阅读笔记06
- 构建之法阅读笔记04
- 阅读笔记[1] ——《Think In Java》 Chapter 13
- Expert C Programming阅读笔记VI
- Storm Trident Example 代码阅读笔记
- 阅读腾讯编程规范的笔记
- ASP.NET Core 源码阅读笔记(2) ---Microsoft.Extensions.DependencyInjection生命周期管理
- SpringMVC学习之HttpServlet 阅读笔记
- 《C++ primer》英文第五版阅读笔记(二十一)——简单语句,语句范围和条件语句
- APUE 阅读笔记(chapter 11&12)——线程
- 《深入理解计算机系统》阅读笔记--进程和线程的区别?