方法深入研究
方法是完成特定功能的代码集。举例来类比一下,比如一台电视,我们开启电视开关之后,我们不需要去理解电视是如何选台、和电视台联系上而播放的步骤,只需要知道会开关电视、选台等就可以了。所以,方法就象一个被封闭的起来的功能操作。
|
封装就是使某些信息隐藏起来,但是,对于一定范围的信息仍是可见的。
封装是面向对象三大特性之一。主要是为了一.避免非法数据的访问;二.保证数据的完整性。
我们可以通过访问修饰符来实现封装。C#支持以下如下访问修饰符:
访问修饰符是一些关键字,用于指定声明的成员或类型的可访问性。
【世间万物皆有一定的存在或活动范围,比如你是中国人,那么你的活动范围大部分在中国,如果你要去国外你就得办个护照。再比如你是郑州海洋馆的一只热带鱼,那么别人要来看你,至少要买张门票。世间万物的这种活动和访问范围的机制使世界可以有条不紊的运作。对象或对象成员属性和方法也需要通过某种机制来约束其访问,所以就有了访问修饰符这些东东。】
本节介绍四个访问修饰符:
n public
n protected
n internal
n private
使用这些访问修饰符可指定下列五个可访问性级别:
声明的可访问性 | 含义 |
public | 访问不受限制。 |
protected | 访问仅限于包含类或从包含类派生的类型。 |
internal | 访问仅限于当前程序集。 |
protected internal | 访问仅限于从包含类派生的当前程序集或类型。 |
private | 访问仅限于包含类型。 |
切记前面有分号,表示分隔.
|
项目背景:对于每位玩过第一人称射击类游戏的朋友来说,CS不能不被反复提起。我们以里面的警察为例,来学习一下访问修饰符符的用处与区别。
解决方案:通过修改访问修饰符,来观察了解访问修饰符的作用范围。
任务1:编写代码,在UltraEdit中编写如下代码,并尝试修改访问修饰符。
/*
功能:实现一款游戏中的一个角色的定义
时间:2015/09/08
作者:张
版本:1.0
*/
usingSystem;
classPolice
{
//属性:Police的成员变量;
private int id; //private:私有的?何为私有:只能被类本身来访问;
private string NickName; //昵称
private string Clothes; //衣服
//行为:成员方法
public void SetInfo() //设置警察信息方法
{
id=888; //这些信息不应该放在Game类里,而应该由Police类的对象来操作;同样访问一个类的成员变量需要,使用对象.成员变量
NickName="菜青虫";
Clothes="绿色迷彩服";
}
public void Display()
{
Console.WriteLine("Id:"+id+"昵称:"+NickName+" 衣服:"+Clothes); //显示一下信息
}
public void Pao() //成员方法定义:[访问修饰符][返回类型]方法名(参数列表){方法代码}
{
Console.WriteLine("跑@....");
}
public void Zou()
{
Console.WriteLine("走#....");
}
public void Sheji()
{
Console.WriteLine("射击DDDDDD");
}
}
class Game
{
static void Main() //程序的入口处
{
Police kiki=newPolice();
//尝试在此处直接访问类的成员变量
//kiki.id=”007”;kiki.NickName=”金头云”
kiki.SetInfo();
kiki.Display();
kiki.Pao(); //对象名.方法()
kiki.Zou();
kiki.Sheji();
Console.ReadLine();
}
}
2.选择文件à保存,以保存该文件,并以Police.cs结束。
3.编译并运行该程序。
4.观察测试不同访问修饰符的区别异同。
|
方法是类的组成元素,而方法本身又是由很多代码组成的,这些代码也可以包含对方法的调用。事实上,我们在前面的章节已经不止一次用过方法了,例如Main(),我们一直在使用这个方法。方法的定义包括
<访问修饰符><返回类型> 方法名 (形式参数列表)
{
方法体
}
方法命名规则和变量名命名规则一样,方法名应该反映对象行为的含义。
方法的参数列表表示方法接受的处理数据,很多时候事务的行为需要一定的处理数据行为才能正常执行。比如说洗衣机的处理数据是要洗的衣服,动物“吃”方法的处理数据是吃的东西等等。参数列表可以是以下形式:
void eat(); //空参数,不需要参数
void eat(string sPork); //一个形式参数sPork (简称形参)
void eat(string sEgg,int iNum); //两个形式参数(sEgg,iNum)
void eat(string sEgg,int iNum,string sAddress);//三个形式参数(sEgg,iNum,sAddress)"
方法调用:eat(“猪肉”) //注意在此处传入的值”猪肉”为实际参数(简称实参)
方法体指要在方法中执行的代码,它是该方法的详细处理步骤。比如洗衣机的“洗衣”方法里面就包括详细洗衣的步骤,比如左转20圈然后右转20圈、洗衣20分钟、甩干等。
返回类型用关键字return,return语句使用时重点要注意以下几个问题:
n 一个方法中可以出现多个return语句,但只有一个会被执行;
n 返回值的数据类型必须与方法定义返回值数据类型相同;
n 如果方法定义了返回值则必须使用return语句进行返回;
n 当遇到return语句时,方法执行将终止,执行被中断并退出方法;
下面来看一下计算器的程序,看是否仍有需要提高的地方。
/*
功能:实现一个非常简单的计算器,根据客户的要求来进行+ - * /运算;
时间:2015/09/09 ;在第三章Demo\Switch基础上完善
作者:张
版本:2.1.3
*/
using System;
class Calc //Calc类完成的功能是什么?用户输入和计算;输入应该放到客户端来实现,但是此处留个小尾巴.到4.0扩展;
{
//1.成员变量
double x,y; //1.声明两个变量
char op; //op表示操作符; //注意比较:int、char、string的使用,使用哪个更好一些。
//2.成员方法
public void Shuru()
{
Console.WriteLine("请输入第一个操作数:");
x=Convert.ToDouble(Console.ReadLine());
Console.WriteLine("请输入第二个操作数:");
y=Convert.ToDouble(Console.ReadLine());
}
public void Jisuan()
{
Console.WriteLine("请输入您要进行的运算:+ - * /");
op=Convert.ToChar(Console.ReadLine()); //因为op是char型的,需要强制转换;
switch(op)
{
case '+':
Console.WriteLine("和为:"+(x+y));
break;
case '-':
Console.WriteLine("差为:"+(x-y));
break;
case '*':
Console.WriteLine("积为:"+(x*y));
break;
case '/':
if(y==0)
Console.WriteLine("除数不能为0");
else
Console.WriteLine("商为:"+(x/y));
break;
default:
Console.WriteLine("错误选择:");
break;
}
}
}
//***************************************客户端类
class CalcGUI
{
static void Main()
{
Calc jisuanqi=new Calc();
jisuanqi.Shuru();
jisuanqi.Jisuan();
}
}
该版本的计算器中已经很好的实现了,业务逻辑(计算功能)和界面(客户端:输入)的功能,但是依据OO的原则,应该具有良好的扩展性和复用性,如果在此时,需要增加新的功能则必须再次修改swich中的内容,并且输出功能也应该在客户端实现。因此,此程序仍有完善的必要。下面请大家做项目2,实现业务逻辑与客户端分离的程序。
|
项目背景:剖析计算器,更好的体会OO扩展性、复用性的好处。
解决方案:使用带返回值的方法来解决.
任务1:编写代码
/*
功能:实现计算器;将计算功能和用户输入分离
时间:2015/09/09 星期二 第二节课
版本:4.1.1
*/
usingSystem;
classCalc
{
//Calc类的成员变量x,y
private double x,y;
private double result; //再增加一个结果变量
public void ShuRu()
{
//在此接受x,y的值
Console.WriteLine("请输入第一个操作数:");
x=Convert.ToDouble(Console.ReadLine());
Console.WriteLine("请输入第二个操作数:");
y=Convert.ToDouble(Console.ReadLine());
}
public double Jia()
{
result=x+y;
return result;
}
public double Jian()
{
result=x-y;
return result;
}
public double Cheng()
{
result=x*y;
return result;
}
public double Chu()
{
if(y==0)
return 0; //注意此处是有问题的,考虑如何修改
else
{
result=x/y;
return result;
}
}
}
/**********客户端类,即界面类***********/
classClacGUI
{
static void Main()
{
Calc jisuanqi; //jisuanqi=?
jisuanqi=new Calc();
jisuanqi.ShuRu();
Console.WriteLine(“和”+jisuanqi.Jia()); //返回double型的和-->数
Console.WriteLine(“差”+jisuanqi.Jian()); //返回double型的差-->数
Console.WriteLine(“积”+jisuanqi.Cheng()); //返回double型的积-->数
Console.WriteLine(“商”+jisuanqi.Chu()); //返回double型的商-->字符串
//doublex,y; //在Main()方法内,所以说不是ClacGUI的成员变量.叫什么呢?(考察的知识点,为变量的作用域)
}
}
任务2:保存程序为Calc4.cs,并编译程序
任务3:运行程序Calc4
任务4:体会本程序与Calc3的区别,考虑改进在何处,是否仍有改进的余地。
任务5:扩充,使用没有返回值的方法,使用返回类型为string的方法如何做?
|
项目背景:剖析计算器,更好的体会OO扩展性、复用性的好处。在原有计算器的基础上增加求平方、求阶乘的功能。
解决方案:使用带返回值及带参数的方法来解决.
任务1:编写代码
/*
功能:实现计算器;将计算功能和用户输入分离
时间:2015/09/09 星期二 第二节课
版本:5.1.1
*/
usingSystem;
classCalc
{
private double result; //再增加一个结果变量
public double Jia(double num1,double num2) //加法运算:形参:num1,num2
{
result=num1+num2;
returnresult;
}
publicdouble Jian(double num1,double num2) //减法运算
{
result=num1-num2;
returnresult;
}
publicdouble Cheng(double num1,double num2) //乘法运算
{
result=num1*num2;
returnresult;
}
publicdouble Chu(double num1,double num2) //除法运算
{
result=num1/num2;
returnresult;
}
publicdouble PingFang(double num1)
{
result=num1*num1;
returnresult;
}
publicdouble NciFang(double num1,double num2) //num1:2的num2:5次方
{
result=1; //定义result为1
for(inti=1;i<=num2;i++)
{
result=result*num1;
}
return result;
}
}
/**********客户端类,即界面类***********/
classClacGUI
{
static void Main()
{
Calc jisuanqi; //jisuanqi=?
jisuanqi=new Calc();
double x,y; //x,y为Main()的局部变量
Console.Write("请输入第一个数字:");
x=Convert.ToDouble(Console.ReadLine());
Console.Write("请输入第二个数字:");
y=Convert.ToDouble(Console.ReadLine());
char op; //op为Main()的局部变量,表示操作符
Console.Write("请输入您要进行的运算:+ - * / ^:"); //^:表示求N次方
op=Convert.ToChar(Console.ReadLine());
switch(op)
{
case '+': //加运算
Console.WriteLine(jisuanqi.Jia(x,y));
break;
case '-':
Console.WriteLine(jisuanqi.Jian(x,y));
break;
case '*': //加运算
Console.WriteLine(jisuanqi.Cheng(x,y));
break;
case '/':
if(y==0)
Console.WriteLine("除数不能为0");
else
Console.WriteLine(jisuanqi.Chu(x,y));
break;
case '^':
Console.WriteLine(jisuanqi.NciFang(x,y));
break;
default:
Console.WriteLine("其他无效选择!!!");
break;
}
}
}
任务2:保存文件,并编译程序
任务3:运行程序
任务4:体会参数在方法中的应用。
|
值参数:声明方法时,不带任何修饰符号的是值参数,一个值参数相当于一个局部变量。当使用值参数时,将会分配一个新的存储位置,将实参的值拷贝到这个位置,并且将此拷贝传递给该方法。允许方法将新值传递给值参数,但是这样的赋值只影响实参的副本,而不影响在方法调用时由调用方法给出的实参。被调用方法不会修改内存中实参的值,所以,使用值参数时可以保证实参值是安全的。
简而言之:值参数只能将值带进方法,不能将值带出方法。
案例:二数字交换
//NotSwap.cs
// 值参数不能影响实参的值
usingSystem;
classTest
{
static void NotSwap(int x, int y)
{
Console.WriteLine("进入NotSwap时:x = {0}, y = {1}", x, y);
int temp = x;
x = y;
y = temp;
Console.WriteLine("退出NotSwap时:x = {0}, y = {1}", x, y);
}
static void Main()
{
int i = 1, j = 2;
Console.WriteLine("执行NotSwap前:i = {0}, j = {1}", i, j);
NotSwap(i, j);
Console.WriteLine("执行NotSwap后:i = {0}, j = {1}", i, j);
}
}
程序运行效果是值参数的值仅仅在方法体内被改变,而不能影响对应的实参的值。
引用参数:
用ref修饰符声明的参数是引用参数。与值参数不同,引用参数不能创建新的存储位置,用参数表示的存储位置就是在方法调用中,作为实际参数对应的变量所表示的存储位置。
变量在作为引用参数传递之前,必须先明确赋值。
简而言之:引用参数既能将值带进方法,也能将值带出方法。
案例:二数交换
//Swap.cs
// 引用参数会影响实参的值
usingSystem;
classTest
{
static void Swap(ref int x, ref int y)
{
Console.WriteLine("进入Swap时:x = {0}, y = {1}", x, y);
int temp = x;
x = y;
y = temp;
Console.WriteLine("退出Swap时:x = {0}, y = {1}", x, y);
}
static void Main()
{
int i = 1, j = 2;
Console.WriteLine("执行Swap前:i = {0}, j = {1}", i, j);
Swap(ref i, ref j);
Console.WriteLine("执行Swap后:i = {0}, j = {1}", i, j);
}
}
输出参数:
用out修饰的参数是输出参数,其他同ref参数一样,在使用之前无须赋值,必须在方法内完成对象的初始化。
简而言之:输出参数不能将值带进方法,只能将值带出方法。
案例:将路径分解为目录名和文件名的方法SplitPath,需要返回目录名和文件名两个值。
//SplitPath.cs
// 利用输出参数来返回多个值
usingSystem;
classTest
{
static void SplitPath(string path, outstring dir, out string name)
{
int i = path.Length;
while (i > 0)
{
char ch = path[i-1];
if (ch == '\\' || ch == '/'|| ch == ':')
break;
i--;
}
dir = path.Substring(0, i);
name = path.Substring(i);
}
static void Main()
{
string dir, name;
SplitPath("c:\\Windows\\System\\hello.txt",out dir, out name);
Console.WriteLine("目录名: {0}",dir);
Console.WriteLine("文件名: {0}", name);
}
}
|
static:用来修饰静态成员和静态方法;
没有使用static修饰的成员和方法为实例成员和实例方法;
区别如下:
ü 静态成员、方法属于类,被类的所有实例共享;实例成员、方法属于对象(类的实例),每个对象都有实例成员的不同副本。
ü 静态成员只标识一个存储位置,无论创建了多少个类的实例,它的静态成员在内存中都只占同一块区域;类的实例成员属于类的实例所有,每创建一个类的实例,都在内存中为实例成员开辟一块区域。
ü 静态成员、方法通过类名.运算符来使用;实例成员、方法通过对象名. 运算符来使用。
ü 静态方法只能访问类中的静态成员,而不能使用实例成员。
在前面的Console类的WriteLine、Write等方法就是静态方法,是通过类Console来引用的。
案例:使用账户Account类获取银行信息的静态方法PrintBankInfo
//Account2.cs
// 演示方法的声明和调用
usingSystem;
classAccount
{
private decimal balance;
private string name;
public Account(string name)
{
this.balance = 0;
this.name = name;
Print("开户", 0);
}
public bool Deposit(decimal amount)
{
if (amount <= 0)
return false;
balance += amount;
Print("存入", amount);
return true;
}
public bool Withdraw(decimal amount)
{
if (amount > balance || amount <=0)
return false;
balance -= amount;
Print("取出", amount);
return true;
}
private void Print(string operation,decimal amount)
{
Console.WriteLine("---------------------------");
Console.WriteLine("姓名: {0}", name);
Console.WriteLine("{0}: {1}",operation, amount);
Console.WriteLine("余额: {0}", balance);
Console.WriteLine("---------------------------");
}
public static void PrintBankInfo()
{
Console.WriteLine("---------------------------");
Console.WriteLine(" XYZ银行, 电话: 12345678");
Console.WriteLine("---------------------------");
Console.WriteLine(" 顾客至上 服务第一 ");
Console.WriteLine("---------------------------");
}
public static void Main()
{
Account.PrintBankInfo();
Account acc = new Account("张三");
bool succeed = acc.Deposit(100);
if (!succeed)
Console.WriteLine("存款失败!");
succeed = acc.Withdraw(37);
if (!succeed)
Console.WriteLine("取款失败!");
}
}
有时候使用静态方法非常方便。对于常用的“工具”方法一般都使用静态方法啦实现,比如Math类。
|
选择题:
1. 说明以下语句是正确的还是错误的
封装意味着防止访问不需要的信息()
2. 请注意以下语句
语句A :静态变量即使在执行它们所属的函数后仍然保持其原值。
语句B:静态函数可以访问静态以及非静态的变量。
以下那项是正确的?
a.A和B都正确
b.A正确,B错误
c.A 错误,B正确
d.A和B都是错误
3.判断以下语句是正确的还是错误的:
在公共访问区分符中,基类的所有公共成员都是受保护的。
抽象代表对象或类的基本特性,这些特性使得它们能够与其他对象或类区别开来。
|
在本章中,我们主要学习了:
u 访问修饰符的分类和使用范围
u 参数化方法的几种方式
u 静态方法和静态变量
|
英文 全文 中文
Public 公共的、公开的
Private 私有的
Protected 受保护的
Internal 内部的
Access 访问
Outside 外部的
Definition 定义
Operator 操作符
Model 模型
Set 设置
Get 得到
Error 错误
Area 区域
Specifier 修饰符
Return 返回
Method 方法
Parameterparameter list 参数
List 列表
Following 下面的
Factorial 阶乘
Out 输出的
Ref reference 引用
Example 例子
Count 计数
|
编写一个程序,该程序要计算用户输入的数的阶乘。请使用静态和静态变量创建此程序。提示:用户输入数的极限是1-20。