• 1. 第3章 值和方法C#程序设计
  • 2. 值类型包括简单值类型和复合值类型。其中简单值类型有( )类型、( )类型、( )类型和( )类型;字符可看作特殊的( )类型。而复合值类型则是简单值类型的组合,包括( )类型和( )类型。 ( )类型的变量直接包含自身的数据。而( )类型的变量则是指向实际数据的地址。如果两个引用类型的变量指向同一个对象,则修改其中的一个,另一个会随之发生变化。 第2章 知识点回顾整数字符布尔实数整数结构枚举值引用
  • 3. 在值类型之间进行转换时,从低精度类型到高精度类型通常是( )转换,反之应是( )转换;在引用类型之间进行转换时,从派生类型到基类型是( )转换,反之应是( )显式转换;从值类型到引用类型的隐式转换叫( )转换,而从引用类型到值类型的显式转换叫( )转换。 隐式转换通常是安全的,显式转换并不能保证总成功,应在需要时进行类型检查。第2章知识点回顾隐式显式隐式显式装箱拆箱
  • 4. 值和方法第3章常量和变量1字段2方法3成员访问限制4本章主要内容
  • 5. 程序处理的是内存中的数据,这些数据的表示形式分为常量和变量。 方法用于对数据进行各种操作,方法代码中出现的变量为局部变量、字段和参数。值和方法第3章
  • 6. 常量 常量指的是固定的值。如: Console.WriteLine(10); //输出整数常量 Console.WriteLine(‘A’); //输出字符常量 Console.WriteLine(“Hello”); //输出字符串常量常量和变量3.1 字符串是一种特殊的引用类型。所有引用类型的常量只有一个,就是空值null,可以将null赋给任何引用类型的变量。
  • 7. 变量 变量是指程序中的数据存储单元,每个变量都是其类型的一个对象,其值在运行时可以被改变。 变量必须先定义后使用。常量和变量3.1
  • 8. 变量的定义:变量使用前必须先定义,定义格式: 数据类型 变量名[=初始值] int x; x=10; double x=5,y=10; //多个同类型变量的声明可合并为一行代码,之间以逗号分隔 常量和变量3.1int x=10;可合并为:
  • 9. 常量和变量3.1int x=10; //变量一经定义,其类型就不能改变 x=new Double(); //错误!整数类型的变量不能变为实数类型从C#3.0开始引入了一个关键字var,用于“隐式”变量声明,即在声明变量时不用明确指定其类型。 var x1=10; var x2=2.5;编译器会自动推断x1为int型,x2为double类型。之后对其使用于标准声明的变量无异
  • 10. double x; //变量x只是声明,未真正分配内存 double y=0.5; //变量y通过常量进行赋值 double z=y+1.2; //变量z通过表达式进行赋值常量和变量3.1如果把此处的y变成x,会出现什么情况?程序将不能通过编译,错误是“使用了未赋值的局部变量”
  • 11. 变量的理解可从4个方面: 变量的名称:存储变量的内存空间的地址的助记符 变量的值:变量名称指向的内存空间中存储的内容,变量使用前必须被赋值。C#中不允许使用未初始化的变量。 变量的数据类型:决定变量可容纳何种类型的数值、数值的取值范围以及可执行何种操作。 变量的作用域:由变量声明的位置决定。在声明所在的语句块中有效。常量和变量3.1
  • 12. x值类型变量 直接把值存放在变量名称标记的存储位置上; 给值类型的变量赋值,采用值拷贝方式。int x;y222int y=2; x=y;常量和变量3.1
  • 13. 引用类型的变量 声明变量时只是分配存放这个引用的存储空间 使用new操作符创建对象并把对象的存储地址赋给引用型变量Contact c;c=new Contact();c?c常量和变量3.1
  • 14. 引用类型的变量 注意“未赋值”和“空值null”之间的区别string s; Console.WriteLine(s); //编译出错,“使用了未赋值的局部变量”常量和变量3.1string s=null; Console.WriteLine(s); // 正确,输出一个空行string s=null; Console.WriteLine(s.ToString()); // 编译通过,运行出错,“未将对象引用设置到对象的实例”
  • 15. 引用类型的变量 对于引用类型的变量,如果未赋值就访问其成员,则代码不能通过编译,错误是“使用了未赋值的变量”;而如果在变量为null时访问其成员,代码能够通过编译,但在程序运行时会发生异常。常量和变量3.1异常类型是:NullReferenceException---“未将对象引用设置到对象的实例”参考程序代码P3_1.cs
  • 16. 字段也称为字段型变量,是对象的数据成员,包括实例字段和静态字段。 实例字段(实例变量) 在类或结构定义的一般数据成员。(不使用static修饰符) 创建类或结构的对象时,字段也就拥有了自己的值。 类的定义中可以为字段指定初始值。也可以使用类的构造函数初始化字段字段3.2如:class Rectangle { public int Width=1; public int Height=1; } Rectangle r1=new Rectangle(); Console.WriteLine(“Width={0}, Height={1}”,Width,Height);注意这种初始化字段的方式只能用于类,不能用于结构
  • 17. 字段型变量(包括实例字段和静态字段) 静态字段(静态变量) 字段定义时增加static修饰符 静态字段归类型本身所有,与类型的具体对象无关。 访问静态成员时,使用: 类型名.静态字段名字段3.2如:class Account { public static string Cur=“人民币”; //静态字段 public decimal money; //实例字段 }Account a=new Account(){money=1000}; Console.WriteLine(“余额:{0}{1}”a.money,Account.Cur);P3_2.cs实例字段通过对象名访问,静态字段通过类型名访问
  • 18. 常数和只读字段 常数字段 用于保存一些不发生变化的数值。使用const关键字修饰字段。 常数字段默认就是静态的,(但不能再增加static修饰符),属于类型本身所有,为类型的每个实例共享。 常数字段必须在定义的同时进行赋值,且所赋的值必须为常量。 访问常数字段时,使用:类型名.常数字段名 不允许修改其值。 常数字段一般都是简单值类型,或是字符串类型。字段3.2如:class Circle { public const double Pi=3.14159; //常数字段 }
  • 19. 常数和只读字段 字段3.2int x=Int32.MaxValue;下列关于常数字段的用法是否正确?Int32.MaxValue=345678;正确! 通过类型访问常数字段错误! 企图修改常数字段的值public const object obj=new object();错误! 常数字段必须赋常数值public const int[] a=new int[]{1,2,3};错误! 不能定义数组型常数字段const修饰符修饰的常数字段一般都是简单值类型或字符串类型。思考为什么?
  • 20. 常数和只读字段 只读字段 用于保存一些不发生变化的数值。使用readonly关键字修饰字段。 只读字段默认是实例字段,也可再使用static修饰符。 常数字段可在定义时赋值,也可在构造函数中初始化。但不能在对象创建表达式中赋值,也不允许在构造函数之外修改其值。 只读字段既可以是值类型,也可以是引用类型。字段3.2如:class Account { public readonly string Cur=“人民币”; //只读字段 public string ID; public decimal money; public void Query() { Console.WriteLine(“余额{0}{1}”,money,Cur); } }
  • 21. 常数和只读字段 字段3.2下列关于只读字段的用法是否正确?private readonly object obj=new object();正确! 只读字段可为引用类型public readonly int[] a=new int[]{1,2,3};正确! 只读字段可为数组Account acc1=new Account(); Console.WriteLine(acc1.Cur); acc1.Cur=“港币”;错误! 不允许在构造函数外修改只读字段Account acc1=new Account(){Cur=“港币”,money=1000};错误! 只读字段不能在对象创建表达式中赋值P3_3.cs
  • 22. 方法代表了对象所能提供的服务。 方法的定义包含4部分:依次是返回类型、方法名、(参数列表)和{执行体}。定义方法的基本格式如下: [修饰符] 返回值类型 方法名(参数列表) { //方法的执行体 }方法3.3
  • 23. 如果省略“修饰符”,默认为private,只能被类型中的其他方法调用。“参数列表”是用逗号分隔的类型、标识符。是形式参数(形参),用以接受实际参数(实参)传给方法的值,可以为空。“返回类型”指定该方法返回数据的类型,可以是任何有效的类型。如果没有返回值,则必须为void。方法3.3
  • 24. 方法的执行体中使用return语句返回一个值。方法3.3如果方法的返回值类型为void,可以使用单独的return语句或者没有return语句。 class Circle { public const double Pi=3.14159; public double R=1.0; public void Scale(double d) { R=R*d; return; //该语句可省略 }public double GetArea( ) { return Pi*R*R; } }该表达式的类型应与方法定义的返回值类型double型一致形式参数
  • 25. 方法的参数是保证不同的方法间互动的重要桥梁,C#中方法的参数类型有4种:值参数 ——值传递 引用型参数——引用传递 输出型参数——引用传递 数组型参数——值传递或引用传递方法3.3
  • 26. 值参数:不含任何修饰符,被调用的方法不会修改实参的值。Swap方法用于交换两个数的值 public static void Swap(double x, double y) { double temp=x; x=y; y=temp; }方法3.3假设Swap方法为Program类中的一个方法成员。
  • 27. 值参数:不含任何修饰符,被调用的方法不会修改实参的值。class Program { static void Main() { double x=5,y=10; Console.WriteLine(“交换前:x={0},y={1}”,x,y); Swap(x,y); Console.WriteLine(“交换后:x={0},y={1}”,x,y); } public static void Swap(double x, double y) { //代码见前页} }输出: 交换前:x=5,y=10 交换后:x=5,y=10方法3.3
  • 28. 引用型参数:以ref关键字修饰参数,将实参的存储地址传递给形参。所以对形参的修改会影响实参。 public static void RefSwap(ref double x, ref double y) { double temp=x; x=y; y=temp; } 方法3.3形参前添加关键字ref ,表示该参数为引用型参数
  • 29. 引用型参数:以ref关键字修饰参数,将实参的存储地址传递给形参。所以对形参的修改会影响实参。 class Program { public static void Main() { double x=5,y=10; Console.WriteLine(“交换前:x={0},y={1}”,x,y); RefSwap(ref x,ref y); //调用方法时,实参前也要加ref关键字 Console.WriteLine(“交换后:x={0},y={1}”,x,y); } }输出: 交换前:x=5,y=10 交换后:x=10,y=5方法3.3P3_6.cs演示了引用型参数使用ref和不使用ref关键字的区别
  • 30. 输出型参数:使用输出型参数可以执行一次方法得到多个返回值。以out关键字修饰参数。与引用型参数的区别在于实参可不进行初始化,而在方法的执行代码中初始化。class Circle { public const double Pi=3.14159; public double R=1.0; public double Compute(out double p) //实例方法 { double t=Pi*R; p=2*t; //圆的周长,存于形参中 return t*R; //返回圆的面积 } }方法3.3形参必须在方法中被赋值。
  • 31. 输出型参数:调用方法时,输出型参数的实参前面也要加上out关键字。static void Main() { Circle c=new Circle(){R=7.2}; double p; double a=c.Compute(out p); Console.WriteLine(“面积{0}, 周长{1}”,a,p); }方法3.3用作输出型参数的实参可以不进行赋值
  • 32. 输出型参数:使用输出型参数可以执行一次方法得到多个返回值。以out关键字修饰参数。与引用型参数的区别在于实参可不进行初始化,而在方法的执行代码中初始化。方法3.3public static bool TryParse( string value, out int result )如前面学习过的各种数据类型的TryParse方法,第二个参数就是输出型参数。
  • 33. 数组型参数:以params关键字修饰参数,根据实参形式选择进行引用传递或者值传递,包含数量可变的参数。 每个方法中只允许定义一个数组型参数,且该参数必须放在参数列表最后。 数组型参数所定义的数组必须是一维数组。 数组型参数不能同时既为引用型参数又为输出型参数。方法3.3改写程序P3_4.cs
  • 34. class Program { static void Main() { int x0=1,x1=3,x2=5; AddFL(x0,x1,x2); Console.WriteLine (“ x0={0} ,x2= {1} ”,x0,x2); int[ ] x={x0,x1,x2}; AddFL(x); Console.WriteLine (“ x[0]={0} ,x[2]= {1} ”,x0,x2); } static void AddFL(params int[] array) { array[0]+=1; array[array.Length-1]+=1; } }输出: x0=1,x2=5 x[0]=2,x[2]=6;方法3.3程序P3_10.cs
  • 35. 将3个变量分别传递给方法时,对每个参数采用的是值传递方式,方法执行代码对形参的修改不会影响实参。将一个数组整体传递给方法时,采用的是引用传递方式,执行代码对形参的修改同时也修改了实参。方法3.3
  • 36. 方法的标识与重载:一个类型中不能有两个标识相同的成员,方法名和参数列表共同组成了方法的标识。因此在同一个类中允许存在两个同名的方法,只要方法的参数列表不完全相同(参数数量或类型不同),这时称该方法具有同名的重载形式。方法3.3
  • 37. class Swapper { public static void Swap (ref double x, ref double y) { double temp=x; x=y; y=temp; } public static void Swap (ref int x, ref int y) { int temp=x; x=y; y=temp; } void Swap (ref float x, ref float y) { float temp=x; x=y; y=temp; } }方法3.3
  • 38. class Program { public static void Main() { int x=50,y=10; Swapper .Swap(ref x,ref y); } }编译时,根据参数数量和类型决定调用类Swapper中的第2个方法方法3.3
  • 39. public static void FA(ref int x,ref int y){} public static void FA(int x,int y){}√public static void FB(int x){} public static int FB(int x){return 0-x;}×public static void FC(int a,ref int b){} public static void FC(int a,out int b){}×方法3.3下列方法的重载是否合法?方法的返回类型不足以对方法进行标识一个为普通类型,另一个为ref或out型,视为不同类型的参数一个为ref类型,一个为out类型,不足以标识不同的方法
  • 40. private void MyMethod2(int i) {} private void MyMethod2(object o) {} public void MyMethod2(out int i) {i=1;}√ private void MyMethod1(int i) {} protected void MyMethod1(int i) {} public int MyMethod1 (int i) {return -1;}×方法3.3
  • 41. 方法3.3可选参数和命名参数——可选参数 C#语言从4.0开始支持可选参数的概念,即允许在方法定义时为参数指定默认值。如果在调用时未指定这些参数值,则这些参数的值就为默认值。class Rectangle { public double Width=1, Height=1; public void Scale(double d1,double d2) { Width*=d1; Height*=d2; } public void Scale(double d1) { Width*=d1; } }
  • 42. 方法3.3可选参数和命名参数——可选参数 以上Scale方法的两种重载形式合并为一个: public void Scale(double d1,double d2=1.0) //使用了可选参数 { Width*=d1; Height*=d2; }调用Scale方法时,为其指定一个或两个参数都是合法的:Rectangle r1=new Rectangle(); r1.Scale(2.0,3.0); r1.Scale(1.5); //相当于r1.Scale(1.5,1.0);
  • 43. 方法3.3可选参数和命名参数——可选参数 使用可选参数要注意:(1)为可选参数指定的默认值必须是一个常量表达式。 (2)可选参数不能是引用型(ref)或输出型(out)参数。 (3)如方法中同时包含必选参数和可选参数,在参数列表中可选参数应放在必选参数之后。 (4)如果方法中使用了多个可选参数,则不能省略前面的参数而去指定后面的参数值。 (5)如果一个类型中同时包含带可选参数和不带可选参数的方法,则不带可选参数的方法优先被调用。
  • 44. 方法3.3可选参数和命名参数——命名参数 C#语言从4.0开始支持命名参数的概念,即在方法调用中同时指定参数的名称和值。可不必考虑方法定义中参数出现的顺序。 r1.Scale(d2:2.0, d1:3.0); r1.Scale(3.0,d2:2.0); //命名参数应出现在非命名参数后面 P3_8.cs演示了可选参数和命名参数结合使用才能发挥更大的作用
  • 45. 方法3.3实例方法和静态方法——实例方法 方法的定义中没有使用static关键字修饰。 在一个类中,实例方法的执行代码可以直接访问静态和非静态成员class Swapper { void Swap (ref double x, ref double y) { double temp=x; x=y; y=temp; } }访问类的实例方法需通过实例进行,如: double x=5,double y=10; Swapper s=new Swapper(); s.Swap(ref x,ref y);
  • 46. 方法3.3实例方法和静态方法——静态方法 方法的定义中使用static关键字修饰。 在一个类中,静态方法的执行代码只能直接访问静态成员,要访问非静态成员需要通过实例。 class Swapper { void static Swap (ref double x, ref double y) { double temp=x; x=y; y=temp; } }访问类的静态方法需通过类型名进行,如: double x=5,double y=10; Swapper.Swap(ref x,ref y);
  • 47. 方法3.3class Account { public static string Cur=“人民币”; public string ID; public decimal Money=100; public void Query ( ) { Console.WriteLine(“卡号{0},余额{1}{2}”,ID,Money,Cur); } public static void Query(Account a) { Console.WriteLine(“卡号{0},余额{1}{2}”,a.ID,a.Money,Cur); } }静态字段实例字段实例方法,可直接访问静态字段和实例字段静态方法,只能直接访问静态字段,对实例字段的访问通过实例
  • 48. 委托与方法调用3.4 将方法作为变量或参数进行传递,在C或C++中是通过指针来封装方法,而C#的实现方式就是委托。委托是面向对象和类型安全的。 委托的使用过程分3步:1、定义委托原型 delegate bool MyDelegate(decimal x);委托的定义类似于方法,使用delegate关键字提示:程序中定义的所有委托类型默认都是System程序集中Delegate类的派生类
  • 49. 委托与方法调用3.4 将方法作为变量或参数进行传递,在C或C++中是通过指针来封装方法,而C#的实现方式就是委托。委托是面向对象和类型安全的。 委托的使用过程分3步:2、创建委托对象(实例化委托) 将某个方法作为参数封装到委托对象的创建表达式中,要求方法的参数和返回类型与委托原型中的定义完全一致。 BankCard c1=new BankCard(); MyDelegate d1=new MyDelegate(c1.Pay); MyDelegate d2=c1.Pay;
  • 50. 委托与方法调用3.4 将方法作为变量或参数进行传递,在C或C++中是通过指针来封装方法,而C#的实现方式就是委托。委托是面向对象和类型安全的。 委托的使用过程分3步:3、通过委托对象调用方法 d1(300);参照实例P3_9.cs 说明委托的使用 实例P3_9_1说明委托对象封装多个方法
  • 51. 对象之间主要通过发送消息进行通信,外部对象要访问器数据信息或与之进行消息通信,就必须有足够的访问权限。 C#中提供的成员访问限制修饰符共有4种: 私有访问(private修饰符) 公有访问(public修饰符) 保护访问(protected修饰符) 内部访问(internal修饰符) 成员访问限制3.5
  • 52. private修饰符只能用于类和结构的成员。如成员未加任何限制修饰符,则默认为private。 对于类或结构的成员,如果定义为私有的,则只有在其所在类或结构中的其他成员可以访问,无法从类或结构的外部进行访问。private修饰符
  • 53. private修饰符class BankCard { string ID; //默认为私有成员 private decimal Money; public void Output() { Console.WriteLine(ID); //在类的内部可以访问私有成员 Console.WriteLine(Money); } } }
  • 54. private修饰符using System; namespace Test5 { class Program { public static void Main() { BankCard card1=new BankCard(); Console.WriteLine(card1. id); //错误:外部类无法访问私有成员 Console.WriteLine(card1.money); //错误:无法访问私有成员 card1.Output(); //正确,Output方法为公有的,可从外部访问 } } }
  • 55. public修饰符可用于类和结构的成员,也可用于命名空间下直接定义的类型。 对于类或结构的成员,如果定义为公有的,则除了自身的成员,外部成员也可以访问。 对于命名空间下直接定义的类型,如声明为公有的,从其他程序集就可以访问这些类型。public修饰符
  • 56. public修饰符using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ClassLibrary1 { public class Class1 { public static void Output() { Console.WriteLine(“济南大学"); } } }创建一个“类库”项目,自动设置为public,以便在其他的程序中调用它。
  • 57. public修饰符调用ClassLibrary1程序集中的定义为public的类Class1中的方法using System; using ClassLibrary1; namespace L1_3 { class Program { static void Main(string[] args) { Console.WriteLine("请选择输出:1.拼音 2.汉字"); if (Console.ReadKey().KeyChar == '1' ) Console.WriteLine(“University of Jinan"); else Class1.Output(); } } }
  • 58. protected修饰符只能用于类的成员,而对结构的成员以及命名空间下直接定义的类型无效。 对于类的成员,如果声明为保护的,则只有其所在类及派生类中的成员可以访问,而无法从类的外部进行访问。protected修饰符
  • 59. protected修饰符 class BankCard { public string ID; protected decimal Money; private string Branch; } class CreditCard:BankCard { public void Qurry() { Console.WriteLine("卡号{0}:余额{1}", ID,Money); //Console.WriteLine("开户行{0}", Branch); //错误! } }class Program { public static void Main() { BankCard card=new BankCard(); Console.WriteLine(card. ID); //Console.WriteLine(card.Money); //Console.WriteLine(card. Branch); } }
  • 60. internal修饰符可用于类和结构的成员,也可用于命名空间下直接定义的类型。 任何一种类或成员,如声明为内部的,则它在本程序集中可以被自由访问,而对其他程序集来说是隐藏的。internal修饰符
  • 61. internal修饰符public class BankCard { public string ID; protected decimal Money; public void Query( ) { …… } internal void Deposit(uint x) { …… } internal void Withdraw(uint x) { …… } } internal class CreditCard:BankCard { }class Program { public static void Main() { BankCard card1=new BankCard(); card1. ID=“001” card1.Query( ); card1.Deposit(1000); CreditCard card2=new CreditCard(); card2. ID=“002”; card2.Query( ); card2.Deposit( 500); } }如果BankCard类、CreditCard类和Program类位于同一程序集。对这些类和成员的访问都可以。如果类Program位于其他程序集,则会出错。
  • 62. internal修饰符using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ClassLibrary1 { internal class Class1 { public static void Output() { Console.WriteLine(“济南大学"); } } }前面建立的类库程序,将类Class1的修饰符由原来默认生成的public修改为internal
  • 63. internal修饰符using System; using ClassLibrary1; namespace L1_3 { class Program { static void Main(string[] args) { Console.WriteLine("请选择输出:1.拼音 2.汉字"); if (Console.ReadKey().KeyChar == '1' ) Console.WriteLine(“University of Jinan"); else Class1.Output(); } } }声明为internal的类在其他程序集中不可访问,即使添加了引用。运行时出错,出错信息: “ClassLibrary.Class1”不可访问,因为它受保护级别的限制
  • 64. 只有protected和internal二者可以同时使用,且只能用于类的成员。此时该成员只有其所在的类及派生类中的成员可访问,而无法从类的外部或其他程序集进行访问。internal修饰符
  • 65. 访问限制修饰符意义public访问不受限制protected访问仅限于所在类或从所在类派生的类型internal访问仅限于本程序集protected internal访问仅限于本程序集中所在类或派生类型private访问仅限于所在类型C#中的访问限制修饰符访问限制级别
  • 66. publicinternal:程序集层次上的访问限制修饰符 publicprotectedprivate:类型层次上的访问限制修饰符 派生类的访问限制级别不能低于基类的访问访问限制级别。 字段的访问限制级别不能低于字段类型的访问限制级别。 方法的访问限制级别不能低于方法参数类型的访问限制级别。 访问限制级别
  • 67. 类和结构中可以定义用于描述状态的字段成员,以及用于行为实现的方法成员,还可以在其中的定义嵌套的类或结构。这些成员的访问权限可以通过访问限制修饰符进行控制。 根据成员是属于类型的实例还是类型本身所有,可以将成员分为非静态成员和静态成员。 根据使用方式的不同,类型的实例可分为常量和变量,它们都必须先定义后使用。 本章小结
  • 68. 谢谢