• 1. 第1章 C#与NET平台介绍 本章目标了解.NET Framework 的结构 理解.NET Framework 的基本概念 CLR JIT CTS MSIL 了解 .NET Framework 命名空间 了解C#程序的基本结构
  • 2. 1 C#语言的由来在过去的二十年里,C和C++已经成为在商业软件的开发领域中使用最广泛的语言。它们为程序员提供了十分灵活的操作,不过同时也牺牲了一定的开发效率。与VB等语言相比,同等级别的C/C++应用程序往往需要更长时间来开发。由于C/C++语言的复杂性,许多程序员都试图寻找一种新的语言,希望能在功能与效率之间找到一个更为理想的权衡点。 VB以牺牲灵活性的代价来提高效率。可是这些灵活性正是C/C++程序员所需要的。这些解决方案对编程人员的限制过多(如屏蔽一些底层代码控制的机制),所提供的功能难以令人满意。这些语言无法方便地同早先的系统交互,也无法很好地和当前的网络编程相结合。
  • 3. 对于C/C++用户来说,最理想的解决方案无疑是在快速开发的同时又可以调用底层平台的所有功能。他们想要一种和最新的网络标准保持同步并且能和已有的应用程序良好整合的环境。另外,一些C/C++开发人员还需要在必要的时候进行一些底层的编程。 C# (C sharp) 是对这一问题的解决方案。C#是一种新的、面向对象的编程语言。它使得程序员可以快速地编写各种基于Microsoft .NET平台的应用程序,Microsoft .NET提供了一系列的工具和服务来最大程度地开发利用计算与通讯领域。
  • 4. C#语言的特点: C#语言是Microsoft发布的一种全新的编程语言,是其.Net战略的一枚重要棋子。 C#语言是面向对象的、现代的编程语言,用C#编写的应用程序可以充分利用.Net的框架体系带来的优点.既可以用来编写基于通用网络协议的Internet服务软件,也可以编写各种数据库、网络服务应用程序和Windows窗口界面程序。 C#继承了C/C++的优点和灵活性,能够充分利用OS系统的底层功能,同时又具备了完全的面向对象特性。可以让初学者直接了解面向对象编程的各种概念,学习现代的编程思想和手段。
  • 5. 代码 被执行C++VB编译器编译器可执行 代码可执行 代码R U N T I M ER U N T I M E2 .NET代码的编译 语言特定的编译器和运行库
  • 6. 代码 被执行C++VB编译器编译器可执行 代码可执行 代码R U N T I M ER U N T I M E IL + 元数据 C L R IL 和 CLR 的使用 Intermediate Language Common Language Runtime
  • 7. 编译执行 .NET 程序MSIL + 元数据机器 代码代码 被执行CLR第一次编译第二次编译.NET 程序被编译两次,第一次编译很慢,而第二次编译较快!语言 编译器.NET 源代码
  • 8. Microsoft 中间语言MSIL + 元数据机器 代码代码 被执行CLRMSIL帮助语言实现互操作IL 不是字节代码,但很接近字节代码。因此,执行应用程序时,IL 到机器码的转换速度非常快!通过 CLR将 MSIL 转换为具体 CPU 的代码CPU 无关的指令集语言 编译器.NET 源 代码
  • 9. 程序集client.execlient.execlientlib.dllclient.execlientlib.netmodule
  • 10. IL语言范例 IL_0000: nop IL_0001: call IL_0006: nop IL_0007: ldc.i4.0 IL_0008: call IL_000d: nop IL_000e: ldnull IL_000f: ldftn IL_0015: newobj IL_001a: call IL_001f: nop IL_0020: newobj IL_0025: call IL_002a: nop IL_002b: newobj IL_0030: throw
  • 11. 通用语言运行时CLRCLR.NET 源 代码MSIL + 元数据机器 代码代码 被执行管理内存易于设计组件和应用程序,其对象可以跨语言互动跨语言集成(特别是跨语言继承)JIT 编译器:一次编译,就可运行在支持运行库的任何 CPU 和操作系统上语言 编译器
  • 12. .NET的语言互操作性: 不同语言编写的代码可互相调用 C#的平台移植性: 框架 JIT编译器(Just In Time, 即时编译器)
  • 13. 3 .NET Framework 简介.NET Framework类似于JVM .NET Framework 两个主要组件: 通用语言运行时 (CLR) 统一的类库集 .NET的类库: 线程 文件输入/输出 (I/O) 数据库支持 XML 解析 数据结构……
  • 14. .NET Framework 的体系结构Visual Studio .NETCLR通用语言运行时 (CLR)Base Class Library: IO, Drawing, ThreadingData and XMLXML Web servicesWindows FormsWeb Forms通用语言规范(CLS)VBC++C#…JScript操作系统.Net Framework 类库 (FCL)
  • 15. CLS和CTS通用语言规范 CLS Common Language Specification 规定所有 .NET 语言都应遵循的规则 生成可与其他语言互操作的应用程序 通用类型系统 (Common Type System, CTS) 包含标准数据类型 包含准则集CLS、CTS 和 MSIL 紧密配合以实现语言互操作性
  • 16. 命名空间就像在文件系统中一个文件夹容纳多个文件一样,可以看作某些类的一个容器。通过把类放入命名空间可以把相关的类组织起来,并且可以避免命名冲突。命名空间既用作程序的“内部”组织系统,也用作“外部”组织系统(一种向其他程序公开自己拥有的程序元素的方法) 使用命名空间:using 类似#include4 命名空间
  • 17. 1.H class A { … }2.H class A { … }3.cpp #include “1.h” #include “2.h” ….3.cpp class A { … } class A { … } …
  • 18. namespace a1 { class A { … } }namespace a2 { class A { … } }3.cs using a1; using a2; a1.A a2.A
  • 19. 常用命名空间命名空间说明System.Drawing 处理图形和绘图,包括打印 System.Data 处理数据存取和管理,在定义 ADO.NET 技术中扮演重要角色System.IO 管理对文件和流的同步和异步访问System.Windows 处理基于窗体的窗口的创建System.Reflection 包含从程序集读取元数据的类System.Threading 包含用于多线程编程的类 System.Collections 包含定义各种对象集的接口和类
  • 20. 5 创建“Hello World”应用程序单击“开始”“程序”“Microsoft Visual Studio .NET 2005”“Microsoft Visual Studio .NET 2005” 此时将打开 VS.NET 2005 的起始页 要创建 C# 控制台应用程序,请从列表中选择 “创建建”“项目”。此时将打开“新建项目”窗口 选择“Visual C# 项目”作为项目类型,选择“控制台应用程序”作为模板
  • 21. C# 模板Console.WriteLine("Hello World");输出
  • 22. HelloWorld.csproj AssemblyInfo.cs Program.cs “Hello World”应用程序文件
  • 23. 编译和执行 C# 应用程序要生成 C# 项目,请从菜单中选择“生成” “生成解决方案” 该过程将编译项目中包括的所有文件,编译结果显示在“输出”窗口中 如果结果显示“生成: 1 成功或最新,0失败,0被跳过”,则说明已经成功生成,然后即可交付该应用程序
  • 24. 要运行刚才生成的应用程序,请从菜单中选择“调试”“开始执行(不调试)” 输出结果显示在 VS.NET 控制台窗口中
  • 25. C# 应用程序文件夹结构Visual Studio .NET 2005 创建一个与项目同名的文件夹,此处为“HelloWorld” 该文件夹包含项目文件“HelloWorld.csproj”和其他关联文件 每个新项目都创建了 bin、obj和Properties 三个文件夹。 Bin和obj这两个文件夹下都有一个 Debug 子目录,其中包含可执行文件 HelloWorld.exe 在“解决方案资源管理器”中启用“显示所有文件”选项,可查看“HelloWorld”项目的结构
  • 26. //这是用 C# 编写的一个简单的 HelloWorld 程序 using System; namespace Notepad { class HelloWorld { static void Main() { Console.WriteLine("Hello World"); } } }声明 HelloWorld 类描述代码的注释Hello World 程序程序入口点, Main 的返回类型为 void控制台类的 WriteLine() 方法用于显示输出结果导入 System 命名空间声明命名空间 Notepad将文件保存为 *.cs
  • 27. 说明: 1.  程序一般包括以下几部分: 名称空间的引用:使用using关键字指明引用的名称空间。 名称空间的声明:使用namespace关键字声明名称空间。 类:使用class关键字声明类。 Main方法:Main方法是C# 程序的入口。 2.  类中包含了程序所要用到的数据和所要执行的方法的定义。每个类中可以有: (1)变量声明 (2)构造函数 (3)方法 (4)属性 (5)事件 3.  一个文件中可以有1个或者多个类。 4. 所有语句都以分号";"结束。
  • 28. C#语言基础第2章
  • 29. 目标在C#中定义变量和常量 使用C#中的基本数据类型 理解装箱和拆箱的概念 使用C#中的运算符,选择结构和循环结构 定义和使用数组,了解结构和枚举 了解C#中的预处理指令 理解C#中常用的字符串处理方法
  • 30. 第一个 C#程序 :HelloWorld using System; class HelloWorld { static void Main( ) { Console.WriteLine(“Hello, World!”); } }HelloWorld.cs
  • 31. 第一个 C#程序 :HelloWorld1: using System; 第 1 行:using 关键字 用来引用.NET框架类库中的资源 通常在程序文件的开头使用 如果程序中需要多种资源,可以使用多次using System 命名空间 System命名空间提供了构建应用程序所需的系统统能的访问
  • 32. 第一个 C# 程序 :HelloWorld2: class HelloWorld 3: { ……….. 8: }第 2,3,8 行:类 在C#或其他面向对象语言中,需要编写类 使用关键字class定义一个类,类的内容放在一对 { }中 示例中定义了一个名为HelloWorld的类
  • 33. 第一个 C# 程序 :HelloWorld4: static void Main( ) 5: { ……….. 7: }第 4,5,7 行:Main 方法 Main 方法是应用程序的入口点,编译器将由该处开始执行程序 方法体也放在一对{ }中 每个C#的应用程序都必须包含Main方法
  • 34. 4: static void Main( ) 5: { ……….. 7: }第 4,5,7 行:Main方法 static表示Main方法是一个全局方法 void表示Main方法没有任何返回值,即返回值是空 参数放在小括号中定义。此处括号内没有内容,表明Main方法没有任何参数第一个 C# 程序 :HelloWorld
  • 35. 6: Console.WriteLine(“Hello, World!”); 第 6 行: 语句 在C#中要执行的指令 语句之间用;分隔,编译器通过分号来区分多个语句 使用大括号{ }标识某个代码块的开始和结束,从而可以对语句进行组合。大括号必须成对匹配。第一个 C# 程序 :HelloWorld
  • 36. 6: Console.WriteLine(“Hello, World!”); 第 6 行: 语句 HelloWorld程序的主要目的就是在屏幕上显示一条问候 WriteLine方法用来将问候语写到标准输出设备上。 Console代表标准输出设备--- 屏幕,它属于System命名空间 WriteLine方法是Console类的方法。访问类或对象的方法使用 点取符.来完成。 字符串用双引号“ ”表示 字符串“Hello,World”是WriteLine方法的参数,表示屏幕输出 的内容第一个 C# 程序 :HelloWorld
  • 37. 系统预定义类型类型描 述范围/精度例子object所有其它类型的最根本的基础类型object o = null;string字符串类型,一个字符串是一个Unicode字符序列string s= "Hello";sbyte8-bit 有符号整数类型–128...127sbyte val = 12;short16-bit有符号整数类型–32,768...32,767short val = 12;int32-bit有符号整数类型–2,147,483,648...2,147,483,647int val = 12;long64-bit有符号整数类型–9,223,372,036,854,775,808 ...9,223,372,036,854,775,807long val1 = 12; long val2 = 34L;byte8-bit无符号整数类型0...255byte val1 = 12; byte val2 = 34U;ushort16-bit无符号整数类型0...65,535ushort val1 = 12; ushort val2 = 34U;uint32-bit无符号整数类型0...4,294,967,295uint val1 = 12; uint val2 = 34U;
  • 38. 系统预定义类型类型描 述范围/精度例子ulong64-bit无符号整数类型0...18,446,744,073,709,551,615ulong val1 = 12; ulong val2 = 34U; ulong val3 = 56L; ulong val4 = 78UL;float单精度浮点数类型1.5 × 10−45 至 3.4 × 1038,7 位精度float val = 1.23F;double双精度浮点数类型5.0 × 10−324 至 1.7 × 10308,15 位精度double val1 = 1.23; double val2 = 4.56D;bool布尔类型类型; 一个布尔类型数据不是真就是假true,falsebool val1 = true; bool val2 = false;char字符类型; 一个字符数据是一个Unicode字符char val = 'h';decimal精确十进制类型,有28个有效位1.0 × 10−28 至 7.9 × 1028,28 位精度decimal val = 1.23M;
  • 39. 字面量类型类别后缀示例/允许的值bool布尔无true或falseint整数无int x = 100uint,ulog整数U或Uuint x = 1000ulong,ulong长整型L或llong x = 100000Lulong无符号长整型ul,uL,Ul,UL,lu,Lu,lU或LUulong x = 4324ulfloat单精度浮点数类型F或ffloat x = 34.76Fdouble双精度浮点数类型D或ddouble x = 763.7245Ddecimal精确十进制类型M或mdecimal x = 1.544Mchar字符无char x = ‘a’string字符串无string =”abc”
  • 40. int: int iMax = int.MaxValue; int pVal = int.Parse(“100”); short i16 = 50; int i32 = i16; i16 = i32; 错误 i16 = (short)i32; decimal decimal iRate = 3.9834M; iRate = decimal.Round(iRate, 2); //四舍五入 iRate = decimal.Remainder(512.0M, 51.0M);
  • 41. bool bool bt = (bool)1; //错误 char string pattern = “123abcd?”; bool bt; bt = char.IsLetter(pattern, 3); bt = char.IsNumber(pattern, 3); bt = char.IsLower(pattern, 3); bt = char.IsPunctuation(pattern, 7); bt = char.IsLetterOrDigit(pattern, 3);
  • 42. single,double float f = 24567.66f; double d = 124.45; If(Single.IsNaN(1/0) {…} 使用Parse转换数字字符串 short shParse = Int16.Parse("100"); int iParse = Int32.Parse("100"); long shParse = Int64.Parse("100"); decimal dParse=decimal.Parse(“99.99"); float sParse=float.Parse(“99.99"); double dParse=double.Parse(“99.99");
  • 43. 字符串字符串直接量 string path; path = @”C:\note.txt”; path = “C:\\note.txt”; 字符串操作 索引字符串中的单个字符 string str = “abcd”; char c = str[0]; 字符串连接 string s1 = “My age = “; int myAge = 28; string cat = s1 + myAge;
  • 44. 字符串操作 抽取和定位子串 string poem = “In Xanadu did Kubla Khan”; string poemSeg = poem.Substring(10); poemSeg = poem.Substring(0,9); int index = poem.IndexOf(“I”); index = poem.LastIndexOf(“n”); 比较字符串 bool isMatch; string title = "Ancient Mariner"; isMatch = (title == "ANCIENT AMRINER"); isMatch = (title.ToUpper() == "ANCIENT MARINER"); isMatch = title.Equals("Ancient Mariner");
  • 45. String常用方法C# 中常用的字符串处理方法: Equals() :比较两个字符串的值是否相等 ToLower():将字符串转换成小写形式 IndexOf():查找某个字符在字符串中的位置 SubString():从字符串中截取子字符串 Join():连接字符串 Split():分割字符串 Trim():去掉字符串两边的空格 ……
  • 46. 实例 将一文件名的扩展名改为.dat。例:1.txt改为1.datusing System; using System.Collections.Generic; using System.Linq; using System.Text; namespace _011 { class Program { static void Main(string[] args) { string filename = @"1.2.txt"; int indexDot = filename.LastIndexOf('.'); string extendName = "dat"; filename = filename.Substring(0, indexDot+1); filename += extendName; Console.WriteLine(filename); } } }
  • 47. String 常用方法应用1已有如下代码,按要求增加功能:static void Main(string[ ] args) { string email; // 电子邮件地址 Console.WriteLine("请输入你的邮箱:"); email = Console.ReadLine(); Console.WriteLine("你的邮箱是 {0}", email); }需实现的功能: 1、 输入 yes 时,程序循环执行 2、 兼容以下各种形式 yes(YES、yes、 YeS …..) 3、 提取邮箱的用户名
  • 48. using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace _011 { class Program { static void Main(string[] args) { while(1) { string email; // 电子邮件地址 Console.WriteLine("请输入你的邮箱:"); email = Console.ReadLine(); Console.WriteLine("你的邮箱是 {0}", email); Console.WriteLine("继续输入邮箱吗?"); string input = Console.ReadLine(); if (input.ToUpper() == "YES") continue; else break; } } } }while (true)
  • 49. String 常用方法应用2输入一个字符串,输出每个单词,重新用下划线连接输出使用 Split() 方法分割字符串 使用 Join() 方法连接字符串
  • 50. Split()方法——分割字符串代码分析splitStrings = inputString.Split(' ');// 将分割后的字符串使用下划线连接在一起 joinString = string.Join("_", splitStrings); 返回值为 字符串数组字符串变量名参数:分隔符(char型)返回字符串静态方法参数1:连接符 参数2:字符串数组关键代码:Join()方法——连接字符串
  • 51. using System; class Program { static void Main(string[] args) { string inputString; inputString = Console.ReadLine(); inputString = inputString.Trim(); string[] splitStrings = inputString.Split(' '); string joinString = string.Join("_", splitStrings); Console.WriteLine(joinString); } }如果字符串两边有空格?
  • 52. Format() 方法两种输出字符串的方式:string name = "李四"; Console.WriteLine("My name is " + name + ",I am" + 18);string name = "李四"; Console.WriteLine("My name is {0} ,I am {1} ", name,18);格式字符串变量列表
  • 53. String类的Format()方法用来格式化字符串关键代码:record = string.Format( "姓名:{0}\n出生年月:{1}\n身高:{2}\n 血型:{3}\n星座:{4}\n最喜欢的食物:{5}", name, birthday, height, bloodType, planet, favourFood); Console.WriteLine("\n这是你的个人档案:"); Console.WriteLine(record);参数与 WritLine() 方法相似
  • 54. Format() 的语法小结string myString = string.Format("格式字符串", 参数列表);string myString = string.Format ("{0} 乘以 {1} 等于 {2} ", 2, 3, 2*3);格式字符串代表参数列表2乘以3等于6包括占位符{x}
  • 55. 小结补充下划线处的占位符,输出五个成语string yi = "一"; string er = "二"; string san = "三"; string word = string.Format( "独 _ 无 _,_ 心 _ 意,垂涎 _ 尺,略知 _ _,举_ 反 _", yi, er, san); Console.WriteLine(word);string yi = "一"; string er = "二"; string san = "三"; string word = string.Format( "独{0}无{1},{2}心{1}意,垂涎{2}尺,略知{0}{1},举{0}反{2}", yi, er, san); Console .WriteLine(word);
  • 56. 枚举[access modifiers] enum [:enum_base] {enum body}class MyApp { enum Fabric : int { Cotton = 1, Silk = 2, Wool = 4 } static void Main() { Fabric fab = Fabric.Cotton; int fabNum = (int) fab; string fabType = fab.ToString(); Console.WriteLine(fab); } } 整型,默认为int默认为internal
  • 57. 枚举和位标志namespace MyHelloApp { class myApp { [Flags] enum Fabric { Cotton = 1, Silk = 2 } static void Main() { Fabric fab1 = (Fabric)3; Console.WriteLine(fab1.ToString()); } } }
  • 58. System.Enum的方法using System; namespace App1 { class myApp { enum Fabric { Cotton = 1, Silk = 2 } static void Main() { string fabStr = "Cotton"; if (Enum.IsDefined(typeof(Fabric), fabStr)) { Fabric fab = (Fabric)Enum.Parse(typeof(Fabric), fabStr); Console.WriteLine(Enum.GetName(typeof(Fabric), 2)); } } } }
  • 59. 变量 static void Main(string[] args) { // 声明布尔型、字符串型、整型、短整型和浮点型变量 bool t = false; short n1 = 30; int n2 = 1200; string str = "jeny"; float n3 = 23.1f; // 显示变量值 Console.WriteLine ("布尔值 = " + t); Console.WriteLine ("短整型值 = " + n1); Console.WriteLine ("整型值 = " + n2); Console.WriteLine ("字符串值 = " + str); Console.WriteLine ("浮点值 = " + n3); }变量中存储的值取决于该变量的类型
  • 60. 常量static void Main(string[] args) { // PI常量PI const float _pi = 3.1415169F; // 由地球引力引起的加速度常量,单位为 cm/s*s const float _gravity = 980; // 钟摆的长度 int length = 60; // 钟摆的周期 double period = 0; // 钟摆周期的计算公式 period = 2 * _pi * Math.Sqrt(length / _gravity); Console.WriteLine ("钟摆的周期为 {0} 秒", period); }声明常量在表达式中使用常量const 关键字用于声明常量
  • 61. 数组数组是同一数据类型的一组值 数组属于引用类型,因此存储在堆内存中 数组元素初始化或给数组元素赋值都可以在声明数组时或在程序的后面阶段中进行 语法: 数据类型[,…] 数组名称 = new 类型 [n,…]; int[] arrayHere = new int [6];
  • 62. 数组static void Main(string[] args) { int count; Console.WriteLine(“请输入准备登机的乘客人数 "); count=int.Parse(Console.ReadLine()); // 声明一个存放姓名的字符串数组,其长度等于乘客人数 string[] names = new string[count]; // 用一个 for 循环来接受姓名 for(int i=0; i
  • 63. 一维数组和多维数组都属于矩形数组,而C#所特有的不规则数组是数组的数组,在它的内部,每个数组的长度可以不同,就像一个锯齿形状。 (1)不规则数组的声明 语法形式: type [ ] [ ] [ ] arrayName ; 方括号[ ]的个数与数组的维数相关。 例如: int [ ] [ ] jagged ; // jagged是一个int类型的二维不规则数组 (2)创建数组对象 以二维不规则数组为例:3.不规则数组
  • 64. struct structEx { public int structDataMember; public void structMethod1() { //structMethod1 实现 } }自定义数据类型 可以在其内部定义方法 无法实现继承 属于值类型 主要结构:struct student { public int stud_id; public string stud_name; public float stud_marks; public void show_details() { //显示学生详细信息 } } 结构数据成员方法所有与 Student 关联的详细信息都可以作为一个整体进行存储和访问
  • 65. C# 的基本数据类型C#中的数据类型分为两个基本类别 值类型 表示实际数据 只是将值存放在内存中 值类型都存储在堆栈中 int、char、结构 引用类型 表示指向数据的指针或引用 包含内存堆中对象的地址 为 null,则表示未引用任何对象 类、接口、数组、字符串
  • 66. C# 的基本数据类型static void Main(string[] args) { // 声明一个值类型的整型数据类型 int value = 130; Console.WriteLine("该变量的初始值为 {0}", value); Test(value); // 由于该数据类型属于值类型,所以将恢复其初始值 Console.WriteLine("该变量的值此时为 {0}", value); } static void Test(int byVal) { int t = 20; byVal = t* 30; }将value的初始值传递给Test()方法不反映已经改变的val值,而保留原始值
  • 67. C# 的基本数据类型static void Main(string[] args) { DataType objTest = new DataType (); objTest.Value = 130; // 传递属于引用类型的对象 Test(objTest); // 由于该数据类型属于引用类型,所以会考虑新处理的值 Console.WriteLine("变量的值为 {0}", objTest.Value); } static void Test(DataType data) { int t = 20; data.Val = temp * 30; }将 DataTypeTest 的引用传递给 Test() 被传递的value在Test()中改变反映已经改变的value值class DataType { public int Value; }
  • 68. 引用类型和值类型
  • 69. System.Object方法  名称说明                                           Equals 已重载。 确定两个 Object 实例是否相等。                                   Finalize 允许 Object 在“垃圾回收”回收 Object 之前尝试释放资源并执行其他清理操作。                                  GetHashCode 用作特定类型的哈希函数。                                  GetType 获取当前实例的 Type。                                   MemberwiseClone 创建当前 Object 的浅表副本。                                           ReferenceEquals 确定指定的 Object 实例是否是相同的实例。                                  ToString 返回表示当前 Object 的 String。
  • 70. 引用类型和值类型的内存分配值类型存储在运行栈,引用类型存储在托管堆 Apparel myApparel = new Apparel(); Apparel myApparel1 = myApparel;Class Apparel { public double Price = 250.0; public string FabType = “Syn”; }栈托管堆
  • 71. 装箱与拆箱装箱即将值类型转换为引用 int age = 17; Object refAge = age 拆箱即将引用类型转换为值类型。 int newAge = (int)refAge; double newAge = (double)refAge; //错误,要具有相同类型
  • 72. 运算符和表达式类别运算符说明表达式算 术 运 算 符+执行加法运算(如果两个操作数是字符串,则该运算符用作字符串连接运算符,将一个字符串添加到另一个字符串的末尾) 操作数1 + 操作数2-执行减法运算 操作数1 - 操作数2*执行乘法运算操作数1 * 操作数2/执行除法运算操作数1 / 操作数2%获得进行除法运算后的余数 操作数1 % 操作数2++将操作数加 1 操作数++或++操作数--将操作数减 1 操作数-- 或--操作数~将一个数按位取反 ~操作数
  • 73. 运算符和表达式类别运算符说明表达式三元运算符(条件运算符) ?:检查给出的第一个表达式 expression 是否为真。如果为真,则计算 operand1,否则计算 operand2。这是唯一带有三个操作数的运算符 表达式? 操作数1: 操作数2
  • 74. 运算符和表达式类别运算符说明表达式比较运算符>检查一个数是否大于另一个数 操作数1 > 操作数2<检查一个数是否小于另一个数 操作数1 < 操作数2>=检查一个数是否大于或等于另一个数 操作数1 >= 操作数2<= 检查一个数是否小于或等于另一个数 操作数1 <= 操作数2== 检查两个值是否相等 操作数1 == 操作数2!= 检查两个值是否不相等操作数1 != 操作数2
  • 75. 运算符和表达式类别运算符说明表达式成员访问运算符. 用于访问数据结构的成员 数据结构.成员 赋值运算符=给变量赋值 操作数1 = 操作数2逻辑运算符 && 对两个表达式执行逻辑“与”运算 操作数1 && 操作数2 ||对两个表达式执行逻辑“或”运算 操作数1 || 操作数2 !对两个表达式执行逻辑“非”运算! 操作数( ) 将操作数强制转换为给定的数据类型 (数据类型) 操作数
  • 76. 运算符和表达式 9-6赋值运算符 (=)变量 = 表达式; 例如: 身高 = 177.5; 体重 = 78; 性别 = “m”;
  • 77. 运算符和表达式 9-7一元运算符 (++/--) Variable ++; 相当于 Variable = Variable + 1; Variable --; 相当于 Variable = Variable - 1;
  • 78. 运算符和表达式运算符计算方法表达式求值结果(设 X = 10)+=运算结果 = 操作数1 + 操作数2X+= 2X=X+212-=运算结果 = 操作数1 - 操作数2X-= 2X=X-28*=运算结果 = 操作数1 * 操作数2X*= 2X=X*220/=运算结果 = 操作数1 / 操作数2X/= 2X=X/25%=运算结果 = 操作数1 % 操作数2X%= 2X=X%20
  • 79. 运算符和表达式C# 运算符的优先级优先级说明运算符结合性1括号( )从左到右2自加/自减运算符++/--从右到左3乘法运算符 除法运算符 取模运算符* / %从左到右4加法运算符 减法运算符+ -从左到右5小于 小于等于 大于 大于等于< <= > >=从左到右6等于 不等于= !=从左到右 从左到右7逻辑与&&从左到右8逻辑或||从左到右9赋值运算符和快捷运算符= += *= /= %= -=从右到左
  • 80. 选择结构语法: if (<条件>) { <语句块> } else { <语句块> }选择结构用于根据表达式的值执行语句if … else条件:只能是bool类型的值
  • 81. 选择结构switch (“cotton”) { case “COTTON”: case “cotton”: … break; case 值3: case 值4: … break; }switch…case 表达式可以是int、字符或字符串 C#不允许从一个case块继续执行到下一个case块。每个case块必须以一个跳转控制语句break、goto或return结束 多个case标签可以对应一个代码块
  • 82. 循环结构循环结构用于对一组命令执行一定的次数或反复执行一组命令,直到指定的条件为真。 循环结构的类型 while 循环 do 循环 for 循环 foreach 循环条件:只能是bool类型的值
  • 83. while 循环反复执行指定的语句,直到指定的条件为真 语法 : while (条件) { // 语句 } break 语句可用于退出循环 continue 语句可用于跳过当前循环并开始下一循环 while 循环
  • 84. do…while 循环 do…while 循环与 while 循环类似,二者区别在于 do…while 循环中即使条件为假时也至少执行一次该循环体中的语句。 语法 : do { // 语句 } while (条件)
  • 85. for 循环for 循环要求只有在对特定条件进行判断后才允许执行循环 这种循环用于将某个语句或语句块重复执行预定次数的情形 语法 : for (初始值; 条件; 增/减) { //语句 }
  • 86. foreach 循环 2-1foreach 循环用于遍历整个集合或数组 语法: foreach (数据类型 元素(变量) in 集合或者数组) { //语句 }
  • 87. foreach 循环2-2static void Main(string[] args) { // 存放字母的个数 int Letters = 0; // 存放数字的个数 int Digits = 0; // 存放标点符号的个数 int Punctuations = 0; // 用户提供的输入 string instr; Console.WriteLine("请输入一个字符串 "); instr = Console.ReadLine(); // 声明 foreach 循环以遍历输入的字符串中的每个字符。 foreach(char ch in instr) { // 检查字母 if(char.IsLetter(ch)) Letters++; // 检查数字 if(char.IsDigit(ch)) Digits++; // 检查标点符号 if(char.IsPunctuation(ch)) Punctuations++; } Console.WriteLine(“字母个数为: {0}", Letters); Console.WriteLine(“数字个数为: {0}", Digits); Console.WriteLine(“标点符号个数为: {0}", Punctuations); }为所有计数器设置初始值接受输入对输入的每一个 字符都进行循环使用了所有输入的字符 之后,循环自动终止
  • 88. using System; public class WriteTest { public static void Main() { int[] array = { 1, 2, 3, 4, 5 }; foreach (int item in array) { Console.WriteLine(item); } } }using System; public class WriteTest { public static void Main() { int[] array = { 1, 2, 3, 4, 5 }; foreach (int item in array) { item += item; Console.WriteLine(item); } } }
  • 89. C#的预处理指令2-1预处理指令是C#编译器在词法分析阶段读取的语句。这些语句可以指示编译器包含或不包含某些代码,甚至可以根据预处理指令的值终止编译。 预处理指令由字符#标识,并且字符#必须是该行的第一个非空字符。 预处理指令最常见的三个用途是:完成条件编译、增加诊断来报告错误和警告、定义代码域。
  • 90. C#的预处理指令2-2 C#预处理命令说明#define #undef用于定义一个符号,后取消对一个符号得定义。如果定义了一个符号,那么在#if指令中使用时这个符号计算为true。#if #elif #else #endif类似C#中得if、elseif和else语句#line改变行号序列,并且可以标识该行的源文件#region #endregion用于指定一个代码块,使用Visual studio.NET的大纲特性时可以展开或折叠这个代码块#error导致编译器报告一个致命错误#warming导致编译器报告一个警告,并继续处理
  • 91. #define CLIENT #define DEBUG using System; public class MyApp { public static void Main() { #if DEBUG && INHOUSE #warning Debug in on. #elif DEBUG && CLIENT #error Debug not allowed in Client Code. #endif } }
  • 92. 控制台输入和输出数据输入(Read,ReadLine) Console.Read ()方法 Console.Read方法用来从控制台读取一个字符,其定义如下: Public static int Read(); Read方法返回所读取的字符的Unicode编码值。 注意: Read方法的返回变量是32位的整数,如果需要得到输入的字符,则必须通过数据类型的显式转换才能得到相应的字符。
  • 93. 控制台输入和输出Console.Read () //ReadTest.cs using System; public class ReadTest { public static void Main() { int i; char ch; i=Console.Read(); ch=(char) i; //显式类型转换 Console.WriteLine(i); Console.WriteLine(ch); } }运行结果: A 65 A
  • 94. 控制台输入和输出Console.ReadLine ()方法 Console.ReadLine方法用来从控制台读取一行字符,定义如下: Public static string ReadLine(); Read方法返回所读取一行字符的字符串。一般情况下,一行输入是指从输入一个字符开始,遇到回车符号为止。
  • 95. 控制台输入和输出//ReadLine.cs using System; using System.Globalization; public class ReadTest { public static void Main() { int i; double d; string str; str=Console.ReadLine(); //由控制台输入整数字符串 i=int.Parse(str); //整数字符串转换为整数 Console.WriteLine(i); str=Console.ReadLine(); //由控制台输入浮点字符串 d=double.Parse(str); //浮点字符串转换为浮点数 Console.WriteLine(d); } }运行结果: 1234 1234 123.456 123.456
  • 96. 控制台输入和输出数据输出(Write WriteLine) Console.Write ()方法 Console.Write方法用来向控制台输出一个字符,但控制台的光标不会移到下一行。其定义如下: public static void Write(XXX value); public static void Write(string format,object o1,……); 注意:格式化format同格式化函数Format中的格式化串类似,其格式如下: {N[,M][:formatstring]} 其中,字符N表示输出变量的序号,M表示输入变量在控制台中所占的字符空间,如果这个数字为负数,则按照左对齐的方式输出,若为正数,则按照右对齐方式输出。
  • 97. 控制台输入和输出//WriteTest.cs using System; public class WriteTest { public static void Main() { int i=32767; double d=456.56789; //由控制台。按照十进制输出整数及浮点数 Console.Write(“i=0x{0,8:X}\td={1,10:F3}”,i,d); //由控制台。按照十六进制输出整数 Console.Write(“i=0x{0,-8:X}\td={1,-10:F3}”,i,d); } } 输出结果: i=0x 7FFF d= 456.568i=0x7FFF d=456.568
  • 98. 控制台输入和输出Console.WriteLine()方法 Console.WriteLine方法用来向控制台输出一行字符,即WriteLine方法在输出信息之后,在信息的尾部自动添加“\r\n”字符,表示回车换行。 public static void WriteLine(XXX value); public static void WriteLine(string format,object o1,……); 注意:格式化format同WriteLine中的格式化参数完全一样。
  • 99. 控制台输入和输出//WriteLine.cs using System; public class WriteTest { public static void Main() { int i=32767; double d=456.56789; //格式化输出 Console.WriteLine(“i=0x{0,8:X}\td={1,10:F3}”,i,d); Console.WriteLine(“i=0x{0,-8:X}\td={1,-10:F3}”,i,d); Console.WriteLine(“i=0x{0,-8:D}\td={1,-10:C3}”,i,d); } } 输出结果: i=0x 7FFF d= 456.568 i=0x7FFF d=456.568 i=32767 d=¥456.568
  • 100. 第3章 C#面向对象设计
  • 101. 结构程序设计的不足—数据与方法分离void SpeedUp(int & speed) { if(speed < 100) ++speed; } void main() { int speed = 0; SpeedUp(speed); }double speed = 0;
  • 102. 结构程序设计的不足—代码不能重用void main() { int doorN_car1 = 2; int color_car1 = 0; int speed_car1 = 100; printf("%d %d %d\n", doorN_car1, color_car1, speed_car1); int doorN_car2 = 4; int color_car2 = 1; int speed_car2 = 80; printf("%d %d %d\n", doorN_car2, color_car2, speed_car2); }
  • 103. C#的面向对象特性所有东西都是对象:变量和方法的集合。 初级特性:OO最基本的概念,即类和对象。 中级特性:OO最核心的概念,即封装、继承和多态。 高级特性:由初级特性和中级特性引出的一些问题,如构造函数的使用、覆盖的规则、静态变量和函数等。
  • 104. 初级特性 面向对象技术最基本的概念是类和对象: – 类是一个样板,以操作、表示和算法的形式完整地定义了一组对象的行为。它通常也是面向对象语言中的模块化、封装和数据抽象的基础。 – 对象是类的一个实例,是一个软件单元,它由一组结构化的数据和在其上的一组操作构成。
  • 105. 抽象数据类型 类实际上为实际的物体在计算机中定义了一种抽象数据类型。 – 抽象数据类型是仅由数据类型和可能在这个数据类型上进行的操作定义的。 – 使用者只能通过操作方法来访问其属性,不用知道这个数据类型内部各种操作是如何实现的。
  • 106. class Car 类 { public int doorN; 对象 public int color; public int speed; public void SpeedUp() 方法 { speed += 10; } } class MyApp { static void Main() { Car car1; car1.doorN = 2; car1.color = 0; car1.speed = 100; 变量 Car car2; car2.doorN = 4; car2.color = 1; car2.speed = 80; } }public double speed;面向对象程序设计—封装、代码重用
  • 107. 3.2 类与对象类:C#所有的代码都是在某一个类中,因此不可能在类之外的全局区域有变量和方法。 对象:C#中的对象相当于一块内存区域,保存对象特有的类中所定义的数据。 引用:C#中对于对象的操作全部通过引用进行。
  • 108. 3.2.1 类的定义 类使用class关键字声明。采用的形式为: [类修饰符] class 类名称[:基类以及实现的接口列表] { 类体 }[;]
  • 109. 类、对象和引用的声明声明一个类:访问修饰字 class 类名{变量声明,方法声明} class Student {long id; // 学号 char gender; //性别 int classID; // 班级号,注意不能用class作属性名 void ChangeClass(int aClassID) //更改班级{… } } 声明一个对象引用:类名引用名Student student; 创建一个对象:new 类构造函数 student = new Student(); //如果缺少这一步编译器会报错 使用对象:引用名. 变量名/方法名(参数) student.id = 200328013203194;
  • 110. 引用与对象举例
  • 111. 引用类似于C++中的对象指针。但又有区别: 在C#中”引用“是指向一个对象在内存中的位置,在本质上是一种带有很强的完整性和安全性的限制的指针。 当声明某个类、接口或数组类型的一个变量时,变量的值总是某个对象的引用或者是null引用。 指针就是简单的地址而已,而引用除了表示地址而外,还是被引用的数据对象的缩影,可以提供其他信息。 指针可以有++、--运算,引用不可以运算。
  • 112. 3.1.2 类的成员 1.类的成员分类 常量:表示与该类相关联的常量值。 字段:即该类的变量。 类型:用于表示一些类型,它们是该类的局部类型。 方法:用于实现可由该类执行的计算和操作。 属性:用于定义一些命名特性,通过它来读取和写入相关的特性。 事件:用于定义可由该类生成的通知。 索引器:使该类的实例可按与数组相同的(语法)方式进行索引。 运算符:用于定义表达式运算符,通过它对该类的实例进行运算。 实例构造函数:用于规定在初始化该类的实例时需要做些什么。 析构函数:用于规定在永久地放弃该类的一个实例之前需要做些什么。 静态构造函数:用于规定在初始化该类自身时需要做些什么。
  • 113. public class Furniture { const double salesTax = .065; private double purchPrice; private string vendor, inventoryID; public Furniture(string vendor, string inventID, double purchPrice) { this.vendor = vendor; this.inventoryID = inventID; this.purchPrice = purchPrice; } public string MyVendor { get { return vendor; } } public double CalcSalesTax(double salePrice) { return salePrice * salesTax; } }类声明常量字段构造函数成员属性方法
  • 114. 2.类成员的可访问性
  • 115. 类或结构的默认访问类型是internal。 类中所有的成员,默认均为private。class Furniture { const double salesTax = .065; private double purchPrice; private string vendor, inventoryID; public Furniture(string vendor, string inventID, double purchPrice) { this.vendor = vendor; this.inventoryID = inventID; this.purchPrice = purchPrice; } public string MyVendor { get { return vendor; } } public double CalcSalesTax(double salePrice) { return salePrice * salesTax; } }internalprivate
  • 116. class MyApp { static void Main() { Furniture f = new Furniture("aaa", "001", 1.2); Console.WriteLine(f.salesTax); Console.WriteLine(Furniture.salesTax); f.purchPrice = 10; string str = f.MyVendor; } }错误
  • 117. 3.1.3 构造函数构造函数是一种用于对象初始化的特殊方法,有以下特点。 –构造函数只能在对象创建时调用,即和new运算符一起被调用。 –构造函数和类具有相同的名字。 –构造函数可以有0个、1个或多个参数。 –构造函数没有返回值。 –每个类至少有一个构造函数,一个类可以有多个构造函数。 –如果没有为类定义构造函数,系统会自动为其定义一个缺省的构造函数。缺省构造函数不带参数,作用是将实例变量都清零。 –一旦为类定义了构造函数,则系统不会再为其定义缺省构造函数。 C#中构造函数有三种:实例构造,私有构造和静态构造
  • 118. 创建对象与构造函数 类声明后,可以创建类的实例,即对象。创建类的实例需要使用new关键字。类的实例相当于一个变量,创建类实例的格式如下: 类名 对象名=new 构造函数(参数类表); 例如: Point myPoint = new Point();
  • 119. 类的构造函数可通过初始值设定项来调用基类的构造函数,例如: public Student(string no, string name,char sex,int age) : base(name, sex,age) { … } 类的构造函数也可通过关键字this调用同一个类的另一个构造函数,例如: public Point() : this(0,20) { … }
  • 120. 构造函数举例class Student { long id; char gender; int classID; public Student() : this(0, 'F', 0) { } public Student(long aID, char aGender, int aClassID) { id = aID; gender = aGender; classID = aClassID; } }
  • 121. 私有构造函数在某些特殊的情况下,使用私有构造函数能够达到意想不到的效果。比如,想建立这样一个类:不允许被其他类实例化,但提供对外的静态接口成员。在.NET框架类库中就存在这样的类,如System.Math类就不能够实例化,它的所有成员都是静态的。 用关键字private修饰的构造函数就是私有构造函数。
  • 122. 下面的代码建立一个无法实例化的类filling: public class filling { private filling() { } //私有构造 public static void happy() { Console.WriteLine ("How happy!"); } public static void sad() { Console.WriteLine ("So sad!!!!"); } } public class MainClass { public static void Main() { //filling f1=new filling(); //不能实例化 filling.happy(); filling.sad(); } }
  • 123. 析构函数C#支持析构函数。虽然C#能够自动进行垃圾回收,但对于某些资源,.Net不知道如何回收,所以需要人工的内存回收。 在.net 编程环境中,系统的资源分为托管资源和非托管资源。 –托管资源,如简单的int,string,float,DateTime 等等,是不需要人工干预回收的。 –非托管资源,例如文件,窗口或网络连接,对于这类资源虽然垃圾回收器可以跟踪封装非托管资源的对象的生存期,但它不了解具体如何清理这些资源。在使用完之后,必须显式的释放他们,否则会占用系统的内存和资源,而且可能会出现意想不到的错误。 .net 中超过80%的资源都是托管资源。
  • 124. 析构函数与Finalize.NET Framework 提供Object.Finalize方法,默认情况下,Finalize 方法不执行任何操作。如果需要可以覆盖Finalize方法。 通过析构函数可以自动生成Finalize方法和对基类的Finalize方法的调用。 public class Chair { public Chair() {...} ~Chair() {...} void Finalize() {…} } 如果试图既编写析构函数又编写Finalize方法,将导致编译器出错。
  • 125. 析构函数Dispose方法意义销毁对象销毁对象调用方式不能被显示调用,在GC回收时被调用需要显示调用 或者通过using语句调用时机不确定确定,在显示调用或者离开using程序块非托管资源:Dispose方法
  • 126. 对象析构举例 • Dispose 方法是编程人员需要立即释放资源时调用,所以在Dispose 方法中调用Close,并通知GC在回收垃圾时不需要再释放资源。 •析构函数是编程人员在没有调用Dispose方法释放资源的情况下由GC在回收垃圾时调用。 1.一般不要提供析构函数,因为它不能及时地被执行; 2.实现Dispose方法的时候,一定要加上“GC.SuppressFinalize( this )”语句。
  • 127. using System; class MyFile { public MyFile() {//…Open File} public void Close() {//…Close File} public void Dispose() { Close(); GC.SuppressFinalize(this); } ~MyFile(){ Close(); } } class MyApp { public static void Main() { MyFile file = new File(); ... file.Dispose(); } }
  • 128. 问题: – 用Student对象保存学生信息,希望每个对象有一个单独的编号。第一个创建的对象编号为0,第二个对象编号为1,以此类推。 – 这就需要有一个所有Student对象都能访问的变量counter,而且变量counter在所有实例中共享。当一个对象创建时,构造函数增加counter值,下一个对象创建时使用增加过的值。 解决方法: – C#编程语言没有这样的全局变量,但类变量是可以从类的任何实例访问的单个变量。 – 类变量在某种程度上与其它语言中的全局变量相似。但仅限于同类型的对象访问。类中的静态问题
  • 129. 静态变量类变量是在所有类的实例(对象)中共享的变量,在变量声明中用static关键字表示。 类变量可以被标记为public或private。如果被标记为public,不需要类的实例就可以访问。 public class Student { private int serialNumber; private static int counter = 0; public Student() { serialNumber = counter; counter++; } }class MyApp { static void Main() { Student stu = new Student(); Console.WriteLine(stu.serialNumber); Student stu1 = new Student(); Console.WriteLine(stu1.serialNumber); } }
  • 130. 静态方法类方法是不需要类的任何实例就可以被调用的方法,在方法声明中用static关键字表示。 类方法只能访问静态变量,访问非静态变量的尝试会引起编译错误。 静态方法不能被覆盖成非静态的。 main是静态的,因为它必须在任何实例化发生前被访问,以便应用程序的运行。 public class GeneralFunction { public static int AddUp(int x, int y) { //静态方法 return x + y; } } public class UseGeneral { public void method() { int c = GeneralFunction.AddUp(9, 10); //调用静态方法 System.Console.WriteLine("addUp() gives " + c); } }
  • 131. 静态构造函数问题:在Student类的例子中,如果希望对象编号不是从1开始,而是从0到1000之间的随机的数开始。 构造函数中虽然可以执行代码,但每个对象创建时都执行,而本例中只希望第一个对象创建时执行。解决: C#中支持静态构造函数,静态构造函数在类中第一个对象初始化或引用任何静态成员之前执行。
  • 132. using System; public class Student { public int serialNumber; private static int counter; static Student() { Random rand = new Random(0); counter = rand.Next(0, 1000); } public Student() { serialNumber = counter; counter++; } } class MyApp { static void Main() { Student stu = new Student(); Console.WriteLine(stu.serialNumber); Student stu1 = new Student(); Console.WriteLine(stu1.serialNumber); } }
  • 133. 静态构造函数用于初始化类。在创建第一个实例或引用任何静态成员之前,将自动调用静态构造函数来初始化类。 using System; class BaseClass { private static int count; static BaseClass() { count = 0; Console.WriteLine("aaaa"); } public BaseClass() { Console.WriteLine("bbbb"); } } class MyApp { static void Main() { BaseClass a = new BaseClass(); BaseClass a1 = new BaseClass(); } } 静态构造函数既没有访问修饰符,也没有参数。 无法直接调用静态构造函数。 在程序中,用户无法控制何时执行静态构造函数。 静态构造函数的典型用途是:当类使用日志文件时,将使用这种构造函数向日志文件中写入项。
  • 134. 3.3 类成员1.字段成员 字段只是类中声明的一个变量,用来在对象中存储信息。 (1).静态字段 使用static关键字修饰的就是静态字段,静态字段属于类而不属于某个类实例,对它的访问使用“类名.静态字段名” ,不能使用“对象名.静态字段名”的格式。 静态字段通常用来保存当前类的实例个数
  • 135. 例3-2:编写一个类,使用静态字段计算类的实例数量。 public class theclass { public theclass() { count++; //计数器增1 } public void showcount() { Console.WriteLine(“实例数为:” + count ); } private static int count=0; //显式赋初值 } public class Test { public static void Main() { theclass class1=new theclass(); class1.showcount(); theclass class2 =new theclass(); class2.showcount(); } }运行结果: 实例数为:1 实例数为:2
  • 136. (2).只读字段 当字段声明中包括readonly修饰符时,该字段成为只读字段。只能在声明只读字段的同时赋初值。其他任何时候都不允许为只读字段赋值。 public class Class1{ public readonly string str1=@”I am str1”;} 执行下面代码: Class1 c1=new Class1(); Console.WriteLine(c1.str1); //c1.str1=“Change readonlyfiled”;错误 如果在类定义中显式添加一个构造函数,可以在构造函数中改变只读字段的值。 Public class Class1 {Public readonly string str1=@”I am str1”; public Class1(){ str1=“initialized by constructor!”;} }
  • 137. 2.方法成员 方法成员的本质就是在类中声明的函数,描述类能够“做什么”。 (1).方法声明格式 [访问修饰符] 返回类型 方法名称 ([参数列表]) { 方法体; } 省略访问修饰符时,默认为private
  • 138. 例如,下面给出一个矩形类的代码,其中声明了三个方法。 public class myRect { private System.Drawing.Point p0; private int a, b; public myRect(int aa,int bb, System.Drawing.Point p) { a = aa; b = bb; p0 = p; } public int Area() //求面积 {Return (a * b);} public int round() //求周长 {Return (2 * (a + b));} public void move(int dx, int dy) //移动顶点 { p0.X += dx; p0.Y += dy; } }using System.Drawing; public class myRect { private Point p0; private int a, b; public myRect(int aa,int bb, Point p) { a = aa; b = bb; p0 = p; } public int Area() //求面积 {Return (a * b);} public int round() //求周长 {Return (2 * (a + b));} public void move(int dx, int dy) //移动顶点 { p0.X += dx; p0.Y += dy; } }
  • 139. (2).方法重载 重载规则: 方法名相同 方法的参数列表不同:参数个数、参数数据类型 例如3-3,下面建立一个类Operate,该类有两个重载方法,分别对整型和浮点型做加法运算。 public class Operate { public long Add(long a, long b) {return(a*b);} public double Add(double a, double b) {return(a+b);} } 当在程序中使用时,会根据实参数据类型自动选择调用。 Operate p = new Operate(); Console.WriteLine(p.Add(3,5)); //返回15 Console.WriteLine(p.Add(1.03,3.25)); //返回4.28
  • 140. (3).静态方法 使用static关键字来修饰。静态方法不需要类实例,直接使用“类名.方法名”格式调用。在静态方法体内不能够直接使用非静态成员,也没有this引用,因为没有实例可以引用。例如3-4 : public class Operate { public long Add(long a, long b) {return(a+b);} public double Add(double a, double b) {return(a+b);} public static void hi() //静态方法 { Console.WriteLine(str); //可以访问静态成员 //Add(3,5); //不能访问实例成员 } private static string str="you use s static method"; } 再main()方法中调用下面的语句:Operate.hi();
  • 141. 3.4 参数传递C#编程语言支持的参数传递方式包括: 1.值传递:方法中的变量是传入变量的一个拷贝,方法中对形参做的修改,不会影响方法外面的实参。 (1) 对于值类型数据,值传递就是传递了变量的值。 (2) 对于引用类型数据,值传递传递的是引用的值,即方法中的形参和方法外的实参将指向同一对象。因此,通过形参也能修改对象的实际内容。 2.地址传递:方法中的变量是传入变量的一个引用,方法中对形参做的修改,也会影响方法外面的实参。 (1) ref:由调用方法初始化参数值。 (2) out:被调用方法初始化参数值,可以不用初始化就作为参数传递给方法。
  • 142. 值传递-值类型
  • 143. 值传递-引用类型
  • 144. 例如3-5: public class valtest1 { public void valdiliver(int i) //值类型参数 { i+=5; Console.WriteLine(i); } public void valdiliver(valtest2 ob) //引用类型参数 { ob.str+="Changed!"; Console.WriteLine(ob.str); } } public class valtest2 { public string str="this string "; }
  • 145. 在Main()方法中执行下面的语句: valtest1 v1=new valtest1(); valtest2 v2=new valtest2(); int ii=5; Console.WriteLine(ii); v1.valdiliver(ii); //值参数的值传递 Console.WriteLine(ii);   Console.WriteLine(v2.str); v1.valdiliver(v2); //引用参数的值传递 Console.WriteLine(v2.str );输出的结果如下: 5 //传递前实参值 10 //形参值 5 //传递后实参值 this string //传递前实参值 this string changed! //形参值 this string changed! //传递后实参值
  • 146. 引用参数-ref 很多情况下,我们要使用参数的引用传递。引用传递是传递变量的地址,使得形参和实参指向同一内存空间,方法中对于形参的修改,实际上就是对于实参的修改。 由调用方法初始化参数值。实参、形参中ref不能省 。
  • 147. public void CalcuArea(long L, ref wid W) { double Area; L+=10; W+=10; Area = (L * W); Console.WriteLine(Area); } 假设过程调用如下: len= 5; wid= 5; CalcuArea(len, ref wid); 调用之后CalcuArea()函数之后: len仍为5 wid变为15 Area=225
  • 148. 输出参数-out 被调用方法初始化参数值,可以不用初始化就作为参数传递给方法。 实参、形参中out不能省 。
  • 149. 例3-6:输出参数的使用。 public class Test { static void SplitPath(string path, out string dir, out string name) { int i = path.Length; while (i > 0) { char ch = path[i – 1]; if (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(dir); Console.WriteLine(name); } } c:\Windows\System\ hello.txt
  • 150. 参数数组在不能确定需要传递多少个参数的时候可以使用params关键字指明一个可变的参数数组。 数组参数的类型必须是一维数组,而且必须是形参表中的最后一个参数。 数组参数始终是值传递方式进行传递,不能将params同ref和out组合。
  • 151. 例如,下面定义一个具有params参数的求平均值函数: public double AVG( params int[ ] Nums) { int Sum=0; int Count=0;   foreach(int n in Nums) { Sum+=n; Count+=1; } Nums[0] = 100; return (Sum/Count); } 调用该函数时,可以采用下面的语句: Class1 a = new Class1(); double d = a.AVG(13,27,33,25,78); 另外,还可以传递一个实际的数组: int[ ] args = {31,41,46,53,28}; double d = a.AVG(args); args[0] = ?
  • 152. 3.5属性成员属性主要用于描述和维护类对象的状态。从客户端看,对属性的访问就好像直接访问public字段成员,但在类内部是通过类方法访问的。 创建一个属性包括两步: 1.声明一个字段来存储属性值 2.编写一个属性声明,提供访问接口属性的建立要使用属性声明,语法如下: [访问修饰符] 类型名 属性名 { get { return 字段; } set { 私有字段 = value; } }
  • 153. (本页无文本内容)
  • 154. public class checkval { private string p_PropVal; //声明一个私有变量p_PropVal public string str1 //声明属性str1 { get //返回存储在私有变量中的属性值 { return p_PropVal; } set //存储属性值到私有变量 { if (Convert.ToString(value).Length <= 10) { p_PropVal = value; } else { Console.WriteLine("too many words"); } } } }
  • 155. 可以说,属性是一种特殊的方法,但属性和方法也有不同之处,主要有: 属性不必使用圆括号,但方法一定使用圆括号。 属性不能制定参数,方法可以指定参数。 属性不能使用void类型,方法则可以使用void类型。 属性使用方法与变量相同。
  • 156. 属性说明 可以创建只读或只写属性,即只有get或set方法。 可以创建静态属性,用static关键字。 静态属性不与特定实例有关联,因此在静态属性的get和set方法内引用this是错误的。 静态属性使用类名访问,并且,与静态属性相配合的私有字段也应该是静态的。
  • 157. 例3-7: public class checkval { private static string p_time="00:00:00";//静态私有字段 public static string mytime //静态属性 { get { return p_time; } } public static string mytime1 //静态属性 { set { p_time = value; } } static void Main() { Console.WriteLine(checkval.mytime); checkval.mytime1 = "10:2:22"; Console.WriteLine("now is " + checkval.mytime); } }输出结果为: 00:00:00 now is 10:2:22
  • 158. 3.3.6 this关键字 this引用的是当前实例。 this关键字是一个隐含引用,它隐含于每个类的成员函数中。 this关键字引用类的当前对象,成员通过this关键字可以知道自己属于哪一个实例。 public class Test1 { public string str; public void f(string str) { this.str = str; } public static void Main() { Test1 test = new Test1(); test.f("aaa"); } } 静态函数没有this关键字。
  • 159. 将对象作为参数传递到其他方法,例如: public class Test1 { public string str = "aaa"; public void f(Test1 test) { str = "bbb"; Console.WriteLine(test.str); } public void f1() { f(this); } public static void Main() { Test1 test = new Test1(); test.f1(); } }
  • 160. 索引器[访问修饰符] 数据类型 this[数据类型 标识符] { get{}; set{}; }语法参数化成员属性:包含set、get方法。 与成员属性不同: 它可以接受1个或多个参数 使用this作为索引器的名字
  • 161. 定义和调用索引器class Photo { string _title; public Photo(string title) { this._title = title; } public string Title { get { return _title; } } }以 Title 属性表示照片将照片存放于数组 photos 中class Album { // 该数组用于存放照片 Photo[] photos; public Album(int capacity) { photos = new Photo[capacity]; }
  • 162. 定义和调用索引器 4-2public Photo this[int index] { get { // 验证索引范围 if (index < 0 || index >= photos.Length) { Console.WriteLine("索引无效"); // 使用 null 指示失败 return null; } // 对于有效索引,返回请求的照片 return photos[index]; } set { if (index < 0 || index >= photos.Length) { Console.WriteLine("索引无效"); return; } photos[index] = value; } }带有 int 参数的 Photo 索引器读/写索引器
  • 163. 定义和调用索引器public Photo this[string title] { get { // 遍历数组中的所有照片 foreach (Photo p in photos) { // 将照片中的标题与索引器参数进行比较 if (p.Title == title) return p; } Console.WriteLine("未找到"); // 使用 null 指示失败 return null; } }带有 string 参数的 Photo 索引器只读索引器
  • 164. 定义和调用索引器static void Main(string[] args) { // 创建一个容量为 3 的相册 Album family = new Album(3); // 创建 3 张照片 Photo first = new Photo("Jeny "); Photo second = new Photo("Smith"); Photo third = new Photo(“Lono"); // 向相册加载照片 family[0] = first; family[1] = second; family[2] = third; // 按索引检索 Photo objPhoto1 = family[2]; Console.WriteLine(objPhoto1.Title); // 按名称检索 Photo objPhoto2 = family[“Jeny"]; Console.WriteLine(objPhoto2.Title); }
  • 165. 中级特性 面向对象技术的三个核心概念: – 封装:将数据和操作组合到一起,并决定哪些数据和操作对外是可见的。 – 继承:父类中的变量和行为,子类可以同样使用。本质是代码重用。 – 多态:由继承引出的一种机制,父类型的引用变量可以指向子类型的对象。
  • 166. 封装 封装把对象的所有组成部分组合在一起,有三个作用 – 隐藏类的实现细节:使用方法将类的数据隐藏起来。 – 迫使用户去使用一个界面去访问数据:定义程序如何引用对 象的数据,控制用户对类的修改和访问数据的程度。 – 使代码更好维护:类的内部实现改变,对外接口可以不变。
  • 167. 继承 继承提供了创建新类的一种方法,继承对开发者来说就是代码共享。 – 通过继承创建的子类是作为另一个类的扩充或修正所定义的一个类。 – 子类从超类(父类)中继承所有方法和变量。 – 子类和超类之间是特化与范化的关系。
  • 168. 子类的声明 语法:子类声明:父类{子类体} 子类可以使用父类的protected和public可见的变量和方法,就像这些变量和方法是自己定义的一样。 C# 中,如果类声明时没有声明父类,那么缺省为Object 类的子类。C#中的所有类都是System.Object类的子类。 C#中,子类只能继承一个父类。class Car { int color; int door; int speed; void PushBreak() { } public void AddOil() { } }class TrashCar : Car { } class MyApp { static void Main() { TrashCar myCar = new TrashCar(); myCar.AddOil(); myCar.PushBreak(); } }
  • 169. 派生类的建立需要注意: (1).派生类会继承基类除了构造函数和析构函数的所有成员。 (2).派生类调用构造函数时,会先调用基类的构造函数。默认调用没有参数的构造函数。 (3).用base关键字显式调用基类构造函数。class Car { public Car(int i) { } } class TrashCar : Car { public TrashCar(int i) { } } class Car { public Car(int i) { } } class TrashCar : Car { public TrashCar(int i):base(i) { } } 错误错误public Car() { }去掉 base(i)
  • 170. (3). 如果需要调用基类中的同名方法,应该使用”base.方法名”来调用。 class Car { public Car() { } protected void f() { Console.WriteLine("aaa"); } } class TrashCar : Car { public TrashCar() { } void f() { Console.WriteLine("bbb"); } public void f1() { base.f(); f(); } } class MyApp { static void Main() { TrashCar myCar = new TrashCar(); myCar.f1(); } }
  • 171. 例3-8 继承格式举例 public class parent //建立基类 { public parent(string str) //基类带参数构造函数 {Console.WriteLine(str);} public void showposition() //基类方法 { Console.WriteLine("基类的位置在(0,0)"); } } public class child:parent //派生子类 { public child():base("调用基类构造") //子类构造函数,调用基类构造函数 {Console.WriteLine("I am child");} public void showposition() //派生类方法 { base.showposition(); //调用基类方法 Console.WriteLine("派生类的位置在(10,10)"); } } 在Main()方法中执行下面的代码: parent prt=new parent("I am a parent"); child chd= new child(); prt.showposition(); chd.showposition ();
  • 172. 输出结果为: I am a parent 调用基类构造 I am child 基类的位置在(0,0) 基类的位置在(0,0) 派生类的位置在(10,10)
  • 173. 例3-9:编写一个程序,计算球,圆锥,圆柱的表面积和体积 using System; namespace ConApp1 { public class Circle { protected double radius; public Circle(double r) {radius=r;} public double GetArea(){return Math.PI*radius*radius;} } public class Sphere:Circle//球体类 { public Sphere(double r):base(r){} public double GetArea(){return (4*base.GetArea());} public double GetVolumn() {return (4*Math.PI*Math.Pow(radius,3)/3);} } public class Cylinder:Circle//圆柱类 { private double height;//添加高度字段 public Cylinder(double r,double h):base(r){height=h;} public double GetArea() {return (2*base.GetArea()+2*Math.PI*radius*height);} public double GetVolumn(){return(Math.PI*radius*radius*height);} }
  • 174. public class Cone:Circle//圆锥类 { private double height;//添加高度字段 public Cone(double r,double h):base(r){height=h;} public double GetArea() {return(Math.PI*radius*(radius+Math.Sqrt(height*height+radius*radius)));} public double GetVolumn() {return (Math.PI*radius*radius*height/3);} } public class Tester { public static void Main() { Circle c1=new Circle(2); Sphere s1=new Sphere(2); Cylinder cd1=new Cylinder(2,10); Cone cn1=new Cone(2,10); Console.WriteLine("s1's serfacearea={0}, volumn={1}",s1.GetArea(),s1.GetVolumn()); Console.WriteLine("cd1's serfacearea={0}, volumn={1}",cd1.GetArea(),cd1.GetVolumn()); Console.WriteLine("cn1's serfacearea={0}, volumn={1}",cn1.GetArea(),cn1.GetVolumn()); Console.ReadLine(); } } }
  • 175. 多态 继承机制引出多态机制 某一类型的引用变量可以指向该类或者其子类的对象。 由于C#中System.Object类是所有类的祖先,所以可以用Object类型的引用指向所有类型的对象。class Car { … } class TrashCar : Car { … } Car car = new TrashCar(); Object obj = new Car(); obj = new TrashCar();
  • 176. 3.5.2 多态性 多态性是指不同的对象收到相同的消息时,会产生不同动作。 C#支持两种类型的多态性: (1)编译时的多态性是通过重载类实现的,系统在编译时,根据传递的参数个数、类型信息决定实现何种操作。 (2)运行时的多态性是指在运行时,根据实际情况决定实现何种操作。C#中运行时的多态性通过虚函成员实现。
  • 177. 编译时多态---重载 重载指在同一个类中至少有两个方法用同一个名字,但有不同的参数。 重载使得从外部来看,一个操作对于不同的对象有不同的处理方法。 调用时,根据参数的不同来区别调用哪个方法。 方法的返回类型可以相同或不同,但它不足以使返回类型变成唯一的差异。重载方法的参数表必须不同。 class Car { int color; int door; int speed; public void PushBreak() { speed = 0; } public void PushBreak(int s) { speed -= s; } }Car car = new Car(); car.PushBreak(); car.PushBreak(2);
  • 178. 运行时多态---动态绑定(虚函数) 动态绑定就是根据对象的类型决定调用哪个方法,而不是引用的类型。 类的方法使用virtual关键字修饰后就成为虚方法,包括两个步骤: (1).对于基类中要实现多态性的方法,用virtual关键字修饰。不允许再有static,abstract或override修饰符。 (2).对于派生类中的同名方法(覆盖) --相同的名称、返回类型和参数表,使用override关键字修饰。不能有new、static或virtual修饰符。
  • 179. 多态---覆盖 C# 中声明覆盖时,父类方法前加virtual关键字,表示该方法可以被覆盖;子类方法前加override,表示将方法覆盖。 当用于子类的行为与父类的行为不同时,覆盖机制允许子类可以修改从父类继承来的行为。
  • 180. 例3-11下面的代码中,子类重写了父类的虚方法sleep()。 public class animal //基类 { public virtual void sleep() //虚方法 { Console.WriteLine("animal all need sleep"); } } public class fish:animal //派生类 { public override void sleep() //重写虚方法 { Console.WriteLine("fish sleeping with eye_open"); } } public class dog:animal //派生类 { public override void sleep() //重写虚方法 { Console.WriteLine("dog sleeping with eye_closed"); } }
  • 181. Main()中的代码: animal[ ] an=new animal[3]; an[0]=new animal(); an[1]=new fish(); an[2]=new dog(); an[0].sleep(); an[1].sleep(); an[2].sleep();输出结果: animal all need sleep fish sleeping with eye_open dog sleeping with eye_closed实现多态性的核心和实质: 使用基类的引用指向派生类的对象,当程序运行时,编译器会自动确定基类对象的实际运行时类型,并根据实际类型调用正确的方法。
  • 182. 例3-12 class GeometricObject { public virtual void draw(){Console.WriteLine("GeometricObject!");} } class Ellipse:GeometricObject { public override void draw(){Console.WriteLine("Ellipse!");} public void getvecter(){} } class Circle:Ellipse { public override void draw(){Console.WriteLine("Circle!");} public double getArea(){return 1.0;} } main()函数中的代码 GeometricObject g=new Circle(); //父类型引用指向子类型对象g.draw(); g.draw(); //draw调用的是哪个类的方法?如果g换成Circle类引用呢? //如果Circle类不覆盖draw方法,调用的是哪个类的方法? double d=g.getArea(); // 编译时能否通过。运行结果: Circle!
  • 183. 重载和覆盖的区别 相同点: 都涉及两个同名的方法。 不同点: 1.类层次 (1).重载涉及的是同一个类的两个同名方法; (2).覆盖涉及的是子类的一个方法和父类的一个方法,这两个方法同名。 2.参数和返回值 (1).重载的两个方法具有不同的参数,可以有不同返回值类型; (2).覆盖的两个方法具有相同的参数,返回值类型必需相同。
  • 184. 方法的隐藏 若覆盖时没有使用virtual和override关键字,则称子类的方法隐藏了父类的方法。此时编译器报警告。若要消除掉警告,可以使用new修饰符。 C# 会根据引用的类型决定调用哪个类的方法。
  • 185. 例3-14对例3-12的方法隐藏 class GeometricObject { public void draw(){Console.WriteLine("GeometricObject!");} } class Ellipse:GeometricObject { public new void draw(){Console.WriteLine("Ellipse!");} public void getvecter() {} } class Circle:Ellipse { public double getArea(){return 1.0;} } main()函数中的代码 GeometricObject g=new Circle(); //父类型引用指向子类型对象g.draw(); g.draw(); //draw调用的是哪个类的方法?如果g换成Circle类引用呢? Circle c = new Circle(); c.draw(); //将类Ellipse中draw()的修饰符改为private后,c.draw()的结果? 运行结果: GeometricObject! Ellipse!
  • 186. 关键字new和override的区别: (1).new修饰的方法表示显式隐藏基类继承的同名方法,不能够用基类的引用访问派生类的new方法。 (2).override表示重写基类的虚方法,可以用基类的引用指向派生类对象来访问派生类的重写方法。
  • 187. 虚属性由于属性的本质就是类中的方法实现,所以,不但能够实现虚方法,还能够实现虚属性。
  • 188. 下面代码中实现一个虚属性round。 public class Square //基类 { public double x; public Square(double x) //构造方法 { this.x = x; } public virtual double Area() //虚方法 { return x*x; } public virtual double round //虚属性 { get { return (4*x); } } } public class Cube: Square //派生类 { public Cube(double x): base(x) //构造方法 {…} public override double Area() //重写方法 { return (6*(base.Area())); } public override double round //重写属性 { get { return (3*base.round); } } }
  • 189. 例3-13利用多态性重新实现例3-9 using System; namespace ConApp1 { public class Circle //基类 { protected double radius; public Circle(double r) {radius=r;} public virtual double GetArea() {return Math.PI*radius*radius;} public virtual double GetVolumn() {return 0.0;} } public class Sphere:Circle//球体类 { public Sphere(double r):base(r){} public override double GetArea() {return (4*base.GetArea());} public override double GetVolumn() {return (4*Math.PI*Math.Pow(radius,3)/3);} }
  • 190. public class Cylinder:Circle//圆柱类 { private double height;//添加高度字段 public Cylinder(double r,double h):base(r){height=h;} public override double GetArea() {return (2*base.GetArea()+2*Math.PI*radius*height);} public override double GetVolumn() {return (Math.PI*radius*radius*height);} } public class Cone:Circle//圆锥类 { private double height;//添加高度字段 public Cone(double r,double h):base(r){height=h;} public override double GetArea() {return (Math.PI*radius* (radius+Math.Sqrt(height*height+radius*radius)));} public override double GetVolumn() {return (Math.PI*radius*radius*height/3);} }
  • 191. public class Tester { public static void Main() { Circle[ ] c=new Circle[3];//建立基类数组 c[0]=new Sphere(2); c[1]=new Cylinder(2,10); c[2]=new Cone(2,20); Console.WriteLine("c[0]'s serfacearea={0}, volumn={1}",c[0].GetArea(),c[0].GetVolumn()); Console.WriteLine("c[1]'s serfacearea={0}, volumn={1}",c[1].GetArea(),c[1].GetVolumn()); Console.WriteLine("c[2]'s serfacearea={0}, volumn={1}",c[2].GetArea(),c[2].GetVolumn()); } } }
  • 192. 密封类密封类不允许派生子类。C#提供一种不能被继承的类,称为密封类。密封类的声明方法是在类名前加上sealed修饰符。修饰符abstract和sealed不能同时使用。
  • 193. 下面的代码建立了一个密封类Runtime。 public sealed class Runtime { private Runtime();// 私有构造不允许其他代码建立类实例 public static string GetCommandLine()// 静态方法成员 { …… // 实现代码 } } public class anotherclass:Runtime //错误,不能继承密封类 { …… //实现代码 }
  • 194. 密封方法还可以在重写基类中的虚方法或虚属性上使用 sealed 修饰符。这将使您能够允许类从您的类继承,并防止它们重写特定的虚方法或虚属性。在下面的示例中,C从B继承,但C无法重写在A中声明并在B中密封的虚函数F。 class A { public virtual void F() { Console.WriteLine("A.F"); } public virtual void F2() { Console.WriteLine("A.F2"); } } class B : A { public sealed override void F() { Console.WriteLine("B.F"); } public override void F2() { Console.WriteLine("A.F3"); } } class C : B { // Attempting to override F causes compiler error CS0239. // protected override void F() { Console.WriteLine("C.F"); } // Overriding F2 is allowed. public override void F2() { Console.WriteLine("C.F2"); } }
  • 195. 类嵌套内层类被看成是外层类的一个成员。 内层类的方法可以访问外层类的私有成员。 类外当需要访问内层类,可以使用“.”符号。 内层类使用private关键字修饰,就相当于外层类的私有成员,不能在外层类之外被访问到。
  • 196. public class Animal { private string str="my name is animal"; //私有成员 public class Horse //嵌套类 { public void Run() { Animal a=new Animal(); Console.WriteLine(a.str); //访问外层类私有成员 Console.WriteLine("Horse run very fast"); } } } 当需要在Animal类之外访问内层类,则使用下面的语句: Animal.Horse hs=new Animal.Horse(); hs.Run();private class Horse
  • 197. 例3-15:下面的代码建立一个类distance,代表平面上任意两点间的距离。该类包含两个成员(点p1、p2),每个成员都是内嵌类point的对象。另外,还包含一个构造函数和一个求两点间距离的方法。 public class distance //定义一个距离类 { public class point //定义一个内嵌类 { public int x; public int y; public point() { Console.Write("x="); x =Convert.ToInt32( Console.ReadLine()); Console.Write("y="); y =Convert.ToInt32( Console.ReadLine()); } } public point p1, p2; public distance(point p11,point p22) //外层类构造 { p1 = p11; p2 = p22; } public double get_dis() { return Math.Sqrt (Math.Pow((p2.x - p1.x),2)+ Math.Pow((p2.y - p1.y) ,2)); } }
  • 198. 在Main()方法中运行下面的程序代码: distance dis; Console.WriteLine("请输入p1、p2两点的坐标:"); dis = new distance(new distance.point(), new distance.point()); Console.WriteLine("p1、p2两点间距离为:" + dis.get_dis());
  • 199. 3.6 抽象类与接口3.6.1 抽象类 类中的方法不提供具体实现,但该类的派生类必须实现这些方法,这些方法在C#中称为抽象方法。 抽象方法必须是一个没有被实现的空方法。包含抽象方法的类称为抽象类,抽象类中也可以包含非抽象方法。 因为抽象类是用来作为基类的,所以不能直接被外部程序实例化,而且也不能被密封。
  • 200. 抽象类通过关键字abstract进行标记将类声明为抽象。 不能创建抽象类的对象,但可以创建抽象类的引用。 一个abstract类可以不包含抽象方法,可以包含非抽象方法和变量。 抽象方法是虚方法的特例。 构造函数和静态方法不能是抽象的。 一个非abstract类不能包含抽象方法。 子类若要覆盖抽象类的抽象方法时,要使用override关键字。abstract class WashingMachine { public WashingMachine()//构造函数 { Console.WriteLine("here is WashingMachine "); } abstract public void Wash(); //抽象方法 abstract public void Rinse(int loadSize); //抽象方法 abstract public long Spin(int speed); //抽象方法 } WashingMachine m = new WashingMachine();抽象方法的定义
  • 201. 对于上面的抽象类WashingMachine,派生类中的抽象方法可以如下实现: class MyWashingMachine : WashingMachine { public MyWashingMachine() { Console.WriteLine("here is MyWashingMachine ");} override public void Wash() { Console.WriteLine("Wash");} override public void Rinse(int loadSize) { Console.WriteLine("Rinse");} override public long Spin(int speed) { Console.WriteLine("Spin"); return (speed*1000);} }当一个类从抽象类派生时,该派生类必须实际提供所有抽象成员的实现,否则,该派生类仍然是抽象类。如下面的示例所示: abstract public Class A { public abstract void F(); //抽象方法} abstract public Class B:A { public void G(){}; //附加方法} public Class C:B { public override void F(){……}} //抽象方法实现
  • 202. 接口的语法结构: [访问修饰符] interface 接口标识符 [:基接口列表] { 接口体; { 接口成员访问权限为public,但不能加访问修饰符 接口成员不能有定义 接口的成员必须是方法,属性,事件或索引器,不能包含常数、字段、运算符、实例构造函数、析构函数或类型。 例: interface IA { void f(); } 错误interface IA { public void f(); }错误interface IA { void f() { } }接口
  • 203. interface IControl { void Paint();//方法成员 int width{get;set;}//属性成员 } interface ITextBox: Icontrol { void SetText(string text);} interface IListBox: Icontrol { void SetItems(string[] items);} Interface IComboBox:ITextBox,IListBox { }
  • 204. 接口实现实现:类要继承某个接口用“:”,在类的定义中给出接口中所定义方法的实际实现。 除显示实现接口外,类中接口实现必须显示声明为public。 public interface Interface1 { void fun1(int i); }//基成员 public interface Interface2 { new void fun1(int i);//隐藏基成员 void M1(int y);//添加新成员M1 } public class cls1:Interface1 { public void fun1(int i){…..} }//实现接口方法 public class cls2:Interface2 { public void fun1(int i){…..} public void M1(int i){…….} }
  • 205. 对接口成员的访问对接口方法的调用与类中的情况相同。如果底层成员的命名与继承而来的高层成员一致,那么底层成员将覆盖同名的高层成员。 但由于接口支持多继承,在多继承中,如果两个父接口含有同名的成员,这就产生了二义性,这时需要进行显式的声明。
  • 206. 显式实现接口假设类实现了两个接口,而这两个接口都有一个名为read的方法声明,在类中该如何实现read方法?
  • 207. 例3-17显示实现接口 public interface Iconnect { void read(); void write(); } public interface Ibook { void read(); } public class myclass:Ibook,Iconnect { public void read() //隐式实现Iconnect的read() {Console.WriteLine("实现Iconnect.read()方法");} void Ibook.read() //显式实现Ibook的read() {Console.WriteLine("实现Ibook.read()方法");} public void write() //实现接口方法 {Console.WriteLine("实现write()方法");} }不能加任何访问修饰符
  • 208. 在main()函数中 //隐式实现的Iconnect的read()方法 myclass cls=new myclass(); cls.read(); //要想显式使用方法,惟一的方法是将对象先转换为接口类型 Ibook ibk=cls as Ibook; if(ibk!=null) { ibk.read(); }运行结果: 实现Iconnect.read()方法 实现Ibook.read()方法
  • 209. 接口使用接口由类实现后,接口成员可以通过对象实例访问,就好像是类的成员一样。 接口与对象:    (1).接口不是类,所以不能用接口创建对象,即不能用new运算符。 x = new Comparable(); //错误    (2).可以声明接口类型的引用,该引用只能指向实现了该接口的对象。 class Student : Comparable {...} Comparable x = new Student();
  • 210. public interface IA { void f(); } class MyClass : IA { public void f() { Console.WriteLine("aaa"); } public void f1() { Console.WriteLine("bbb"); } } class MyApp { static void Main() { IA obj = new MyClass(); obj.f(); obj.f1(); } }错误 接口引用只能调用实现的接口成员
  • 211. 3 接口的作用如老师和学员都可以收作业, 那么收作业的方法应该放在哪个类?A:Teacher类B:Student类C:两个都放D:Person类E:重新定义造成代码冗余如果增加一个工程师类,他不会收作业如果继承这个类,Person类怎么办?调用收作业的方法不需要改变自定义一个接口来实现IHomeworkCollector
  • 212. public interface IHomeworkCollector { void CollectHomework(); }public class Student : Person, IHomeworkCollectorclass Student : Person, IHomeworkCollector { …. public void CollectHomework() { MessageBox.Show("报告老师!作业收集完毕!"); } } class Teacher : Person, IHomeworkCollector { … public void CollectHomework() { MessageBox.Show(“同学们,请交作业!"); } }1、定义一个收作业的接口2、在有收作业功能的类中实现该接口3、不同的类收作业方式的不同
  • 213. 1、接口作为参数使用private void DoCollectHomework(IHomeworkCollector collector) { collector.CollectHomework(); }无论谁收作业这里都不需要做任何改变2、接口作为返回值使用scofield = new Student(); DoCollectHomework(scofield);private IHomeworkCollector CreateHomeworkCollector(string type){ switch (type) { case "student": collector = new Student("Scofield", Genders.Male, 28, "越狱狱"); break; … } return collector }返回一个实现该接口的对象IHomeworkCollector collector = CreateHomeworkCollector(“student”); collector.CollectHomework();
  • 214. 作为返回值和参数的意义接口作为参数 传递给它的值为实现该接口的对象 接口作为返回值 返回一个实现了该接口的对象
  • 215. 例3-16使用接口技术实现不同对象的面积求和using System; public interface ICalAreaAndVolumn //声明接口 { double GetArea(); } public class Sphere : ICalAreaAndVolumn //球类 { private double radius; public Sphere(double r) { radius = r; } public double GetArea() { return (4 * Math.PI * radius * radius); } } public class Cylinder : ICalAreaAndVolumn //圆柱类 { private double radius, height; public Cylinder(double r, double h) { radius = r; height = h; } public double GetArea() { return (2 * Math.PI * radius * radius + 2 * Math.PI * radius * height); } } public class Cone : ICalAreaAndVolumn //圆锥类 { private double radius, height; public Cone(double r, double h) { radius = r; height = h; } public double GetArea() { return (Math.PI * radius * (radius + Math.Sqrt(height * height + radius * radius))); } }
  • 216. public class MyApp { public static double SumAreas(ICalAreaAndVolumn[] array) { double tot = 0.0; for (int i = 0; i < array.Length; i++) { tot += array[i].GetArea(); } return tot; } public static void Main() { Sphere sp = new Sphere(2); Cylinder cd = new Cylinder(2, 10); Cone cn = new Cone(2, 20); ICalAreaAndVolumn[] array = { sp, cd, cn }; Console.WriteLine("total arears = {0}", SumAreas(array)); } }
  • 217. is运算符is 运算符可以检查对象与类之间的关系,形式为:if ( obj is classname )…… 当obj为classname类或其子类的对象时,运算返回true。 如果引用没有指向对象,编译时报错。 可用于确定接口是否可用 Sphere obj = new Sphere(1); //如果Sphere实现了ICalAreaAndVolumn接口 if(obj is ICalAreaAndVolumn) {...}
  • 218. using System; class Class1 { } class Class2 { } class IsTest { static void Test(object o) { Class1 a; Class2 b; if (o is Class1) { Console.WriteLine("o is Class1"); } else if (o is Class2) { Console.WriteLine("o is Class2"); } else { Console.WriteLine("o is neither Class1 nor Class2."); } } static void Main() { Class1 c1 = new Class1(); Class2 c2 = new Class2(); Test(c1); Test(c2); Test("a string"); } }o is Class1 o is Class2 o is neither Class1 nor Class2.
  • 219. as运算符  as 运算符完成的功能等价于:先用is检查,再执行对象类型转换。 如果类型不兼容,as运算返回null。 Sphere obj = new Sphere(1); ICalAreaAndVolumn myICal; myICal = obj as ICalAreaAndVolumn; //如果Sphere实现了ICalAreaAndVolumn接口 if(myICal != null) {...}
  • 220. using System; class Class1 { } class Class2 { } class MainClass { static void Main() { object[ ] objArray = new object[6]; objArray[0] = new Class1(); objArray[1] = new Class2(); objArray[2] = "hello"; objArray[3] = 123; objArray[4] = 123.4; objArray[5] = null; for (int i = 0; i < objArray.Length; ++i) { string s = objArray[i] as string; Console.Write("{0}:", i); if (s != null) { Console.WriteLine("'" + s + "'"); } else { Console.WriteLine("not a string"); } } } }0:not a string 1:not a string 2:'hello' 3:not a string 4:not a string 5:not a string
  • 221. 接口和抽象类的对比抽象类接口不 同 点用 abstract 定义用 interface 定义只能继承一个类可以实现多个接口非抽象派生类必须实现抽象方法实现接口的类必须实现所有成员需要override实现抽象方法直接实现相同点不能实例化包含未实现的方法派生类必须实现未实现的方法
  • 222. 结构结构是一种用struct声明的自定义数据类型。它和类相似,可以包含构造函数,字段,属性,方法等。 一般情况下结构中只是一些数据,要是需要定义方法,一般将它定义为类。 结构不支持继承,但可继承接口。 访问修饰字 struct 结构名[:接口] { 结构体 }
  • 223. 下面的结构包含三个成员: struct SimpleStruct { private int xval; //私有字段 public int X //属性 { get { return xval;} set { if (value < 100) xval = value;} } public void DisplayX() //方法 { Console.WriteLine("The stored value is: {0}", xval);} }  
  • 224. 例3-18 public struct Point { public int x, y; public Point(int x, int y) //构造函数必须带参数,且必须对字段进行赋值 { this.x = x; this.y = y; } public void draw() { Console.WriteLine("结构对象创建之后才能调用"); } }
  • 225. class MainClass { public static void Main() { Point pt1; //不用new建立结构对象 pt1.x = 10; //初始化结构对象,才能使用,否则出错 pt1.y = 20; Point pt2=new Point(20,20); //用new建立结构对象 Point pt3=new Point( ); //调用了系统提供的默认构造函数   // 打印显示 Console.WriteLine("pt1:"); Console.WriteLine("x = {0}, y = {1}", pt1.x, pt1.y); Console.WriteLine("pt2:"); Console.WriteLine("x = {0}, y = {1}", pt2.x, pt2.y); } }
  • 226. 对于结构的使用需要注意以下几点: 1.结构不能包含显式的无参数构造函数。 2. 显式定义的构造函数必须带参数。 3.对于结构中的实例字段成员,不能在声明时赋值初始化。 4.在声明了结构类型后,可以使用new运算符创建结构对象。如果不使用new,那么在初始化所有字段前,字段保持未赋值状态且对象不可用。 5.结构是值类型,类是引用型。 6.结构不支持继承,但可继承接口。
  • 227. 以下任何一条,就应该使用类: 1.需要派生其他类型。 2.需要继承。 3.该类型作为方法参数传递。因为结构是值类 型,每次调用都要创建结构的副本。但结构放在数组中例外。 4. 该类型用作方法的返回类型。
  • 228. 3.7 委托与事件委托是一种可以指向方法的引用,可以理解为一种函数指针,是类型安全的。它类似于C++中的函数指针,通过对于方法特征和返回值类型的声明,封装了具有相同特征和返回类型的方法。 使用委托需要三个步骤: 声明委托类型 创建委托实例 向委托实例注册方法
  • 229. 委托Multiply(int,int) { …. } Divide(int,int) { …. } 在运行时确定调用哪种方法委托和方法必须具有相同的签名--- public delegate Call(int n1, int n2); ---
  • 230. class Delegates { // 委托定义 public delegate int Call(int n1, int n2); class Math { // 乘法方法 public int Multiply(int n1, int n2) { return n1*n2; } // 除法方法 public int Divide(int n1, int n2) { if(n2!=0) return n1/n2; } }static void Main(string[] args) { // 委托的对象 Call objCall; // Math 类的对象 Math objMath = new Math(); // 将方法与委托关联起来 objCall = new Call(objMath.Multiply); // 将委托实例化 result = objCall(4, 3); System.Console.WriteLine("结果为 {0}", result); } 将方法与委托关联起来
  • 231. 事件抢答者 宣布人抢答者“请听题~”集中注意力聆听其他人事件源事件的发布者事件的订阅人不关心未订阅该事件定义事件 为对象订阅该事件 将发生的事件通知给订阅人
  • 232. 定义事件[访问修饰符] event 委托名 事件名; 语法public delegate void delegateMe(); private event delegateMe eventMe;
  • 233. 事件订阅对象eventMe += new delegateMe(objA.Method); eventMe += new delegateMe(objB.Method);去掉事件 eventMe -= new delegateMe(objA.Method); eventMe -= new delegateMe(objB.Method);
  • 234. 通知订阅对象 if(condition) { eventMe(); }调用订阅特定事件的对象的所有委托
  • 235. 示例class ClassA { public void DispMethod() { Console.WriteLine(“Class A 已接到 NotifyEveryOne 事件的通知!"); } } // 第二个类 class ClassB { public void DispMethod() { Console.WriteLine(“Class B 已接到 NotifyEveryOne 事件的通知! "); } }
  • 236. 示例class Delegate { // 定义委托 public delegate void MeDelegate(); // 定义事件 public event MeDelegate NotifyEveryOne; public void Notify() { // 如果事件不为 null if(NotifyEveryOne != null) { Console.WriteLine("触发事件:"); // 触发事件 NotifyEveryOne(); } } }
  • 237. 示例class TestEvents { [STAThread] static void Main(string[] args) { // 委托的对象 Delegate objDelegate = new Delegate(); // ClassA 的对象 ClassA objClassA = new ClassA(); // ClassB 的对象 ClassB objClassB = new ClassB(); // 订阅该事件 objDelegate.NotifyEveryOne += new Delegate.MeDelegate(objClassA.DispMethod); objDelegate.NotifyEveryOne += new Delegate.MeDelegate(objClassB.DispMethod); objDelegate.Notify(); } }
  • 238. 第4章C#高级编程
  • 239. 调试应用程序开发可以安装在客户端机器上应用程序必须 无错误 无故障 可靠 稳健 查找和排除错误或故障称为调试
  • 240. 调试的必要性 计算机化的计费系统在事物处理过程中,系统显示错误消息系统发生故障X必须重新输入全部信息在部署应用程序前必须先对其进行调试
  • 241. 错误类型错误类型语法错误逻辑错误运行时错误语法错误、缺少括号等 在编译时确定 易于确定错误的算法导致错误结果、公式错误等 在执行过程中确定 难以调试内存泄漏、以零作除数、异常 在运行时确定 难以调试
  • 242. 调试过程调试器观察程序的运行时行为 跟踪变量的值确定语义错误的位置查看寄存器的内容查看内存空间
  • 243. 调试过程暂停可在代码中插入“断点”,以便在特 定行处暂停执行该代码
  • 244. 调试过程右键单击所需代码行,以设置断点选择“插入断点”
  • 245. 调试过程选择“调试” “继续”以便继续执行程序
  • 246. 调试过程.NET 集成开发环境Debug模式Release模式
  • 247. VS.NET 中的调试工具“局部变量”窗口
  • 248. VS.NET 中的调试工具“监视”窗口
  • 249. VS.NET 的调试工具 “快速监视”对话框
  • 250. VS.NET 中的调试工具 “即时”窗口
  • 251. VS.NET 中的调试工具 跨语言调试 调试使用 .NET 框架编写的应用程序以及 Win32 本机应用程序 加入正在运行的程序 调试多个程序 Visual Studio .NET 调试器的功能
  • 252. 异常帐户姓名 余额300123小王300124小黄7000311320小李网上银行某学生小王转帐5000到其朋友小李的帐面上04500数据库系统将查询发送到数据库中tranfer_money() { sendquery(); } ……. ……. 余额= 4500-5000 程序崩溃拒绝交易错误 系统出现故障 “C#”中的异常
  • 253. C# 中的异常处理... INPUT Divisor IF Divisor = 0 THEN Result = Divident/Divisor ....代码片段 1输入除数结果 =_024GOTO PREVIOUS LINE触发异常处理程序
  • 254. C# 中的异常处理... INPUT Divisor Result = Divident/Divisor ....代码片段 1“用户自定义”错误检查机制难以检查输入的任何“特殊字符”IF Divisor = 0 THEN GOTO PREVIOUS LINE IF Divisor < 0 THEN PRINT “无效输入” 运行库运行库应当提供“错误检查机制”
  • 255. 错误与异常错误:可预见,如信用卡号格式不对或口令不对。可由程序代码进行排除。 异常:与程序无关的外部原因造成。如数据表不可用或硬件故障等。
  • 256. System.Exception
  • 257. System.Exception 属性Message获取描述当前异常的消息。 Source获取或设置导致错误的应用程序或对象的名称(程序集的名称)。 TargetSite获取引发当前异常的方法。 StackTrace获取当前异常发生时调用堆栈上的帧的字符串表示形式。
  • 258. System.Exception 在 C# 程序中,引发异常共有以下两种方式使用显式 throw 语句来引发异常。在此情况下,控制权将无条件转到处理异常的部分代码 使用语句或表达式在执行过程中激发了某个异常的条件,使得操作无法正常结束,从而引发异常Try...Catch...Finally
  • 259. try 和 catch 块 滤水器filter_water() { try { water(); } catch { impurities.Show(); } } ……. …….过滤水杂质//程序代码 //错误处理代码 try { //程序代码 } catch (IOException E) { //错误处理代码 }
  • 260. try 和 catch 块 try { //程序代码 } catch (IOException E) { //错误处理代码 } 引发I/O 设备异常
  • 261. try 和 catch 块 try { //程序代码 } catch( E) { //错误处理代码 } 可处理系统中的任何一种异常System.Exception
  • 262. try 和 catch 块 if (grade< 0 && grade > 150) { throw new InvalidNumberInput (grade+ “不是合法的成绩”); }throw 可用来引发自定义异常“InvalidNumberInput”
  • 263. 使用 finallytry { //程序代码 } catch { //错误处理代码 } finally { //finally 代码 }无论有否异常该代码都会执行
  • 264. 多重 catch 块 try { //程序代码 } catch (IOException E) { //错误处理代码 } catch (OutOfMemoryException E) { //错误处理代码 }用于捕捉两种异常的“catch”块
  • 265. using System; public class TestExcep { public static int Calc(int j) { return 100 / j; } } class MyApp { public static void Main() { TestExcep exTest = new TestExcep(); try { int dZero = TestExcep.Calc(0); Console.WriteLine("Result: {0}", dZero); } catch (DivideByZeroException ex) { Console.WriteLine("ex.Message: {0}", ex.Message); Console.WriteLine("ex.Source: {0}", ex.Source); Console.WriteLine("ex.TargetSite: {0}", ex.TargetSite.ToString()); Console.WriteLine("ex.StackTrace: {0}", ex.StackTrace); } catch (Exception ex) { Console.WriteLine("General " + ex.Message); } finally { Console.WriteLine("Cleanup occurs here"); } } }ex.Message: 试图除以零。 ex.Source: 005 ex.TargetSite: Int32 Calc(Int32) ex.StackTrace: 在 TestExcep.Calc(Int32 j) 位置 E:\我的桌面临时文件夹\实验编程\ C Sharp\005\Program.cs: 行号 96 在 MyApp.Main() 位置 E:\我的桌面临时文件夹\实验编程\C Sharp\005\Program.cs:行号 107 Cleanup occurs here 请按任意键继续. . .
  • 266. 如何创建定制异常类using System; public class NoDescException : ApplicationException { public NoDescException() { } public NoDescException(string message) : base(message) { } public NoDescException(string message, Exception innerEx) : base(message, innerEx) { } } public interface IFun1 { string ShowMe();} public interface IFun2 { string ShowMe(); } class Circle : IFun1 { public string ShowMe() { return "Circle-IFun1"; } } public class ObjShowMe { public static void ShowMe(object obj) { if (!(obj is IFun1 && obj is IFun2)) { throw new NoDescException("Interface not implemented for " + obj.ToString()); } } } public class MyApp { static void Main() { Circle myCir = new Circle(); try { ObjShowMe.ShowMe(myCir); } catch (NoDescException ex) { Console.WriteLine(ex.Message); } } }
  • 267. 未处理异常当CLR找不到处理异常的catch过滤器时 using System; class MyApp { public static void Main() { try { int dZero = 1/0; } finally { Console.WriteLine(“finally"); } } }
  • 268. 定制处理未处理异常终止应用程序之前记录有关异常的信息。如果有足够的有关应用程序状态的信息,则可以采取其他措施,如保存程序数据以便于以后进行恢复。 建议谨慎行事,因为未处理异常时可能会损坏程序数据。 没有通用的方法适用于所有C#程序。 Windows窗体应用: Applicatioin.ThreadException += new ThreadExceptionEventHandler(method); Windows控制台应用: Thread.GetDomain().UnhandledException += new UnhandledExceptionEventHandler(method);
  • 269. using System; public class TestExcep { public static int Calc(int j) { return 100/j; } } public class UnForgiven { public static void MyUnhandleMethod(object sender, UnhandledExceptionEventArgs e) { #if DEBUG Console.WriteLine("Debug: " + e.ToString()); #else Console.WriteLine("Release: " + e.ToString()); #endif } } class MyApp { public static void Main() { Thread.GetDomain().UnhandledException += new UnhandledExceptionEventHandler(UnForgiven.MyUnhandleMethod); try { int dZero = TestExcep.Calc(0);} finally { Console.WriteLine("a"); } } }s
  • 270. 定制类中实现System.Object方法ToString():默认情况下,返回类名。应覆盖此方法,显示出对象与访类其他实例不同的特有内容。 Equals():定制类需定义“相等”的含义:可能两个对象有相同的字段值变相等,也可能引用了相同的内存地址才相等。
  • 271. 定制ToString()默认返回. 定制ToString方法实例: using System.Text; using System; public class Chair { private double myPrice; private string myVendor, myID; public Chair(double price, string vendor, string sku) { myPrice = price; myVendor = vendor; myID = sku; } public override string ToString() { StringBuilder chairSB = new StringBuilder(); chairSB.AppendFormat("ITEM = Chair"); chairSB.AppendFormat("\tVENDOR = {0}", myVendor); chairSB.AppendFormat("\tPRICE = {0}", myPrice.ToString()); return chairSB.ToString(); } static void Main() { Chair myChair = new Chair(120.0, "Broyhill", "60-1222"); Console.WriteLine(myChair.ToString()); } }
  • 272. 定制Equals()比较两个引用类型的对象时,当它们指向相同的对象,返回true。 如果基于值来比较对象时,必须覆盖该方法。String类就是这样一个例子,其虽然是引用类型,但它会基于字符串中的字符串完成比较。
  • 273. using System; public class Chair { private double myPrice; private string myVendor, myID; public Chair(double price, string vendor, string sku) { myPrice = price; myVendor = vendor; myID = sku; } public override bool Equals(object obj) { if (obj == null) return false; if (this.GetType() != obj.GetType()) return false; Chair otherObj = (Chair)obj; if (!myVendor.Equals(otherObj.myVendor)) return false; if (!myPrice.Equals(otherObj.myPrice)) return false; if (!myID.Equals(otherObj.myID)) return false; return true; } public override int GetHashCode() { return myID.GetHashCode(); } static void Main() { Chair myChair = new Chair(120.0, "Broyhill", "60-1222"); Chair newChair = new Chair(120.0, "Broyhill", "60-1222"); bool eq = myChair.Equals(newChair); Console.WriteLine(eq.ToString()); } }
  • 274. 覆盖GetHashCode()GetHashCode方法为对象生成一个Int32类型的散列码。.NET要求两个相同的对象必须有相同的散列码,不同的对象不保证有不同的散列码。 Equals方法必须和GetHashCode方法成对出现。
  • 275. 克隆来创建对象副本Object中的MemberwiseClone():默认返回对象的一个副本,是一种浅拷贝。 可以实现自已的克隆方法进行深拷贝。 不能克隆基本类型。可克隆的类必须实现ICloneable接口。 public interface ICloneable { Object Clone(); }
  • 276. using System; public class Chair : ICloneable { private double myPrice; private string myVendor, myID; public Upholstery myUpolstery; public Chair(double price, string vendor, string sku) { myPrice = price; myVendor = vendor; myID = sku; } public Object Clone() { return MemberwiseClone(); } } public class Upholstery { public string fabric; public Upholstery(string fab) { fabric = fab; } } class MyApp { static void Main() { Chair myChair = new Chair(120.0, "Broyhill", "60-1222"); Chair chairClone = (Chair)myChair.Clone(); bool isEqual; isEqual = Object.ReferenceEquals(myChair, chairClone); Console.WriteLine(isEqual.ToString()); isEqual = Object.ReferenceEquals(myChair.myUpolstery, chairClone.myUpolstery); Console.WriteLine(isEqual.ToString()); } }
  • 277. 什么是序列化Profile对象界面语言:英语 RssFeed对象 存储 介质存储序列化是将对象的状态存储到特定存储介质中的过程… 代理服务器
  • 278. 特性[Serializable] abstract class FeedBase标识这个类是可序列化的可序列化就是这个类的一个特性描述性关键字 对程序中的元素如:类、字段、方法、属性 命名时以Attribute结尾: SerializableAttribute 使用时省略Attributepublic sealed class SerializableAttribute 特性其实是一个类
  • 279. 可在类成员附加[NonSerialized]属性,将其排除在串行化范围之外。 public class Chair { [NonSerialized] public double myPrice; public string myVendor, myID; public Chair() {} public Chair(double price, string vendor, string sku) { myPrice = price; myVendor = vendor; myID = sku; } }
  • 280. 使用序列化fileStream = new FileStream("profile.bin", FileMode.Create); BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(fileStream, Profile);[Serializable] abstract class FeedBase要存储的对象标记为可序列化,包括他的父类和属性的类使用二进制方式存储对象二进制格式化器将对象以二进制方式格式化为流Serialize ( Stream serializationStream, Object graph) 流对象Serialize的用法:
  • 281. 序列化的过程格式化程序 对象可否序列化子类成员 可否序列化将对象格式化写入存储介质异常退出序列化对象中的子类成员 YYNN
  • 282. 反序列化把Profile对象存储成文件,怎么取出来呢?读取反序列化则是从特定存储介质中的数据重新构建对象的过程 存储 介质Profile对象界面语言:英语 RssFeed对象 … 代理服务器 数据转换为对象
  • 283. 使用反序列化将序列化好的Profile数据反序列化为对象fileStream = new FileStream("profile.bin", FileMode.Open); BinaryFormatter bf = new BinaryFormatter(); Profile = (Profile)bf.Deserialize(fileStream); 将指定流反序列化类型转换public Object Deserialize ( Stream serializationStream ) Deserialize的用法:流对象需要类型转换
  • 284. using System; using System.Runtime.Serialization.Formatters.Binary; using System.IO; [Serializable] public class Chair { public double myPrice; public string myVendor, myID; public Chair() {} public Chair(double price, string vendor, string sku) { myPrice = price; myVendor = vendor; myID = sku; } } class MyApp { static void Main() { Chair myChair = new Chair(100.0, "Broyhill", "10-09"); FileStream fs = new FileStream(@"C:\chairs.dat", FileMode.Create); BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(fs, myChair); fs.Close(); Chair newChair = new Chair(); fs = new FileStream(@"C:\chairs.dat", FileMode.Open); newChair = (Chair)bf.Deserialize(fs); Console.WriteLine(newChair.myPrice.ToString() + " " + newChair.myVendor + " " + newChair.myID); } }
  • 285. 串行化事件事件属性OnSerializing[OnSerializing]OnSerialized[OnSerialized]OnDeSerializing[OnDesializing]OnDeSerialized[OnDeSerialized]这些事件处理程序在被串行化的对象中实现,必须满足以下两个要求: 必须为方法附加与事件相关的属性 方法必须有以下签名: void (StreamingContext context) 例如: [OnDeSerialized] void OnMyDeserialized(StringContext context) { } 注:需加上using System.Runtime.Serialization;
  • 286. 处理串行化对象的版本变化如果删除了原对象的一个字段,格式化器只是忽略逆串行化流中的多余数据。[Serializable] public class Chair { public double myPrice; public string myVendor, myID; …. }[Serializable] public class Chair { public string myVendor, myID; … }
  • 287. 如果原对象增加了新字段,格式化器发现新字段后,会抛出异常(.NET2.0)。[Serializable] public class Chair { public double myPrice; public string myVendor, myID; …. }[Serializable] public class Chair { public string myVendor, myID; … }可以为新字段附加[OptionalField]属性。格式器会为新字段指定一个默认值。[Serializable] public class Chair { [OptionalField] public double myPrice; public string myVendor, myID; …. }
  • 288. 还可以利用逆串行化事件为新字段赋值。 [OnDeserialized] void OnMyDeserialized(StreamingContext context) { if(myVendor == “Lane”) finish=“OaK”; else finish=“Cherry”; }
  • 289. 反射可以ILDasm反编译工具浏览一个dll和exe的构成这种机制叫做反射(Reflection)应用程序或dll类的属性类的方法应用程序信息…用于在运行时通过编程方式获得类型信息反射
  • 290. 现场演示通过代码演示获取dll的版本号using System.Reflection;class Program { static void Main(string[] args) { string version = Assembly.LoadFile(@"D:\MyNewsReader.exe") .GetName().Version.ToString(); Console.WriteLine(version); } }引入命名空间 反射
  • 291. 什么是设计模式模式就是得到很好研究的范例走为上 围魏救赵 声东击西 设计模式是软件开发过程中经验的积累 特定问题的经过实践检验的特定解决方法
  • 292. 简单工厂模式简单工厂模式的原理父类产品 子类产品A 子类产品B 子类产品C 工厂 客户
  • 293. 简单工厂模式工厂类:担任这个角色的是工厂方法模式的核心,含有与应用紧密相关的商业逻辑。工厂类在客户端的直接调用下创建产品对象,它往往由一个具体的类实现。 抽象产品角色:担任这个角色的类是由工厂方法模式所创建的对象的父类,或她们共同拥有的接口。一般由接口或抽象类实现。  具体产品角色:工厂方法模式所创建的任何对象都是这个角色的实例,由具体类实现。
  • 294. 简单工厂模式优缺点 模式的核心是工厂类,这个类负责产品的创建,而客户端可以免去产品创建的责任,这实现了责任的分割。但由于工厂类集中了所有产品创建逻辑的,如果不能正常工作的话会对系统造成很大的影响。如果增加新产品必须修改工厂角色的源码。
  • 295. 设计模式的意义提高软件的可复用性灵活,适应软件设计的变化面向对象在实际应用中集中体现
  • 296. 简单工厂模式实例父类产品 子类产品A 子类产品C 工厂 客户 public interface IApparel { string ShowMe(); }public class SportShirt: IApparel { public string ShowMe() { return (“Sports Shirt”); } }public class DressShirt: IApparel { public string ShowMe() { return (“Dress Shirt”); } }public class ApparelFactory { public IApparel CreateApparel (string apptype) { switch(apptype) { case “DRESSSHIRT”: return new DressShirt(); case “SPORTSSHIRT”: return new SportsShirt(); } } return null; } }
  • 297. ApparelFactory = new ApparelFactory(); IApparel obj1 = factory.CreateApparel(“DRESSSHIRT”); IApparel obj2 = factory.CreateApparel(“SPORTSSHIRT”); string shirtType = obj1.ShowMe();
  • 298. 为什么需要抽象工厂设计模式父类产品子类产品A 子类产品B 子类产品C 工厂客户:简单工厂设计模式原理结构 什么是抽象工厂设计模式
  • 299. 什么是抽象工厂设计模式抽象工厂 实体工厂2实体工厂1抽象产品A抽象产品B实体产品B1实体产品B2实体产品A1实体产品A2客户 :抽象工厂设计模式原理结构
  • 300. 什么是抽象工厂设计模式抽象工厂角色:担任这个角色的是工厂方法模式的核心,它是与应用系统的商业逻辑无关的。通常使用接口或抽象类实现。 具体工厂角色:这个角色直接在客户端的调用下创建产品的实例。这个角色含有选择合适的产品对象的逻辑,而这个逻辑是与应用系统的商业逻辑紧密相关的。通常使用具体的类实现。 抽象产品角色:担任这个角色的类是抽象工厂方法模式所创建的对象的父类,或它们共同拥有的接口。通常使用接口或抽象类实现这一角色。 具体产品角色:抽象工厂模式所创建的任何产品对象都是某一具体产品类的实例。这是客户端最终需要的东西。通常使用具体类实现这个角色。
  • 301. 什么是抽象工厂设计模式1、提供一系列相互依赖对象的创建工作 2、封装对象常规的创建方法(new) 3、提供统一调用数据访问方法的方式 4、避免调用数据访问方法和具体对象创建工作的紧耦合
  • 302. 什么是抽象工厂设计模型提供一个创建一系列相关或相互依赖对象的接口,无需指定它们具体的类 客户请求 抽象工厂抽象产品如何用抽象工厂设计模式构建我们的数据访问层?
  • 303. 使用抽象工厂模式 一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的。 这个系统的产品有多于一个的产品族,而系统只消费其中某一族的产品。 同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。
  • 304. 如何使用抽象工厂设计模式1、用抽象工厂生产抽象产品 2、用实体工厂生产实体产品 3、用抽象产品提供实体产品访问接口 4、用实体产品实现自己的功能:抽象工厂设计模式中各个象的主要功能、职责
  • 305. 应用示例礼服衬衫实体工厂抽象产品抽象工厂 运动衫实体工厂运动衫 实 体产品礼服衬衫实 体产品客户public abstract class AppFactory { public abstract IApparel CreateApparel(); }public class DressShirtFactory : AppFactory { public override IApparel CreateApparel() { return new DressShirt(); } }public class SportShirtFactory : AppFactory { public override IApparel CreateApparel() { return new SportsShirt(); } }
  • 306. public class ApparelCollector { public void CollectApparel(AppFactory factory) { IApparel apparel = factory.CreateApparel(); } } AppFactory factory = new DressShirtFactory(); IApparel obj = new ApparelCollector().CollectApparel(factory);
  • 307. 第5章集合类及其接口
  • 308. System.Array 简介 3-2 数组定义:数据类型[ ] 数组名称;int[] MyArray = {1,2,3,4,5,6,7};MyArray[0], MyArray[1], MyArray[2]…………MyArray[6] MyArray [0] = 604 System . Array
  • 309. System.Array 简介 3-3System.ArrayArray是抽象的基类,提供 CreateInstance 方法来创建数组Array obj = Array.CreateInstance(typeof(string),10); Array obj1 = Array.CreateInstance(typeof(string),2,3,4);
  • 310. System.Array 的属性和方法 属性Length方法BinarySearchClearCopyRankIsReadOnlyIsFixedSizeCopyToCreateInstanceGetLengthGetLowerBoundGetUpperBoundGetValueIndexOfLastIndexOfReverseSetValueSort
  • 311. 示例 2-1static void Main(string[] args) { //构建 objNames 数组 Array objNames = Array.CreateInstance (typeof(string),5); //初始化值 objNames.SetValue(“A",0); objNames.SetValue(“B",1); objNames.SetValue(“C",2); objNames.SetValue(“D",3); objNames.SetValue(“E",4); Console.WriteLine(“数组值"); for(int ctr = 0 ; ctr < 5; ctr++) { Console.WriteLine(“元素 {0}: {1}",ctr+1, objNames.GetValue(ctr)); }使用 GetValue() 方法检索数组值使用 SetValue() 方法存储字符串将 objNames 实例化为字符串对象并且其中存放 5 个元素
  • 312. 示例 2-2 Console.WriteLine(“\n数组中元素的总数是 {0}",objNames.Length.ToString()); //输出数组秩 Console.WriteLine("\n数组秩是 {0}",objNames.Rank.ToString()); //反转数组并输出 Array.Reverse(objNames); Console.WriteLine(“\n反转数组后"); for(int ctr = 0 ; ctr < 5; ctr++) { Console.WriteLine(“元素 {0}: {1}",ctr+1, objNames.GetValue(ctr)); } 显示 objNames数组的长度显示 objNames数组秩反转数组元素 反转后的数组元素列表课堂练习: 这段代码用For Each结构怎么写?
  • 313. 列表、队列、栈和哈希表数组的局限性: 元素个数固定,且必须在创建数组时知道元素个数 元素类型必须相同 只能通过索引访问数组元素 .NET 中的集合类型 (System.Collections) ArrayList  Queue Stack  HastTable
  • 314. System.Collections接口图
  • 315. 列表、队列、栈和哈希表列表、队列、栈和哈希表是应用程序中管理数据的常见方式类特点示例、用途ArrayList有序的对象列表邮箱:可以在任何位置插入和删除元素Queue先进先出的对象集合排队买票,处理器队列Stack先进后出的对象集合一摞盘子,后缀表达式Hashtable一对(key,object)元素的集合,通过 key 可以访问到指定的元素通过书籍的 ISBN码找到书
  • 316. ArrayList 类ArrayList 很类似数组,但是 ArrayList 类没有固定大小;可以根据需要不断增长 默认大小为16个元素,当添加第17个元素时会自动扩展到32个 可以显式地指定其容量 可以存储不同类型的元素, 因为所有ArrayList中的元素都是对象(System.Object) ArrayList 的方法: Add(object) 把一个对象添加到 ArrayList 的末尾 Insert(index,object) 在指定位置插入一个对象 Remove(object) 移除一个对象 RemoveAt(index) 移除一个对象 Clear() 移除所有元素 Sort 对ArrayList 中的元素进行排序
  • 317. ArrayList 类ArrayList 示例:int i = 100; double d = 999.88d; string s = "Hello World"; DateTime time = DateTime.Now; ArrayList myList = new ArrayList(); myList.Add(i); myList.Add(d); myList.Add(s); myList.Add(time); myList.insert(0, 200); myList.RemoveAt(0);
  • 318. Queue 类 5.2.3队列(Queue) 对象按照先进先出,先来先服务的原则 对象按顺序存储在默认大小为32的缓冲区中;当缓冲区空间不足时,按增长因子(2.0)创建一个新的缓冲区,并将现有对象拷贝到新缓冲区中(开销大) Queue的方法: Enqueue Dequeue Peek Clear Contains man3Queueman2man1
  • 319. using System; using System.Collections; public class SamplesQueue { public static void Main() { Queue myQ = new Queue(); myQ.Enqueue("The"); myQ.Enqueue("quick"); myQ.Enqueue("brown"); myQ.Enqueue("fox"); // Removes an element from the Queue. Console.WriteLine("(Dequeue)\t{0}", myQ.Dequeue()); Console.WriteLine("(Peek) \t{0}", myQ.Peek()); Console.WriteLine(myQ.Contains("The")); } }
  • 320. Stack 类栈(Stack) 后进先出,最后插入的对象位于栈的顶端 Stack的方法 Push Pop Peek Clear Containsbook3book2book1Stack栈顶
  • 321. using System; using System.Collections; public class ShowStack { public static void Main() { Stack myStack = new Stack(); myStack.Push(100); myStack.Push(200); myStack.Push(300); PrintValues(myStack); myStack.Pop(); Console.WriteLine(myStack.Peek().ToString()); } public static void PrintValues(IEnumerable myCollection) { IEnumerator myEnumerator = myCollection.GetEnumerator(); while (myEnumerator.MoveNext()) { Console.WriteLine(myEnumerator.Current.ToString()); } } }对不一个新的集合,Current指向的是首元素的前一个位置。一定要先MoveNext将迭代器 指向最开始元素。
  • 322. HashTable 类哈希表(Hashtable) 由一对(key , value) 类型的元素组成的集合 所有元素的 key 必须唯一 key ->value 是一对一的映射,即根据key就可以立刻在集合众找到所需元素 中国城市天气预报 书目信息 〈key〉 〈value〉 〈key〉 〈value〉 北京 “晴” ISBN-0110 “红与黑” 上海 “小雨” ISBN-0210 “荆棘鸟” 广州 “阴天” ISBN-9106 “简爱”
  • 323. HashTable 类Hashtable方法: Add(key, value) 根据key而不是根据索引查找,因此速度很快 示例: Hashtable ht = new Hashtable(); //创建HashTable //增加对象 ht.Add("Beijing", "Sunny"); ht.Add("ShangHai", "Rainy"); ht.Add("Guandong", "Cloudy"); Ht[“Guandong”]="Cloudy"; //读对象 string bjWeather = ht[“Beijing”];
  • 324. ContainsKey方法 bool keyFound; if(ht.ContainsKey(“Beijing”)) keyFound=true; else keyFound=false; 列举HashTable中的Keys foreach(string key in ht.Keys) {MessageBox.Show(key);} 列举HashTable中的Values foreach(string value in ht.Values) {MessageBox.Show(value);} 列举HashTable中的Keys和Values foreach(DictionaryEntry de in ht) { MessageBox.Show(de.Key + “ “ + de.Values); }
  • 325. 数组和集合的比较数组声明了元素类型,但集合没有,因为集合中所用元素都存储为对象 数组的大小是固定的,不能增加和减少;而集合类可根据需要动态调整大小 检索元素的方式不同
  • 326. 自定义集合是指定制实现System.Collections提供的集合接口的集合。下面以IEnumerable集合接口为例自定义集合。 IEnumerable接口定义: public interface IEnumerable { Ienumerator GetEnumerator(); } 实现IEnumerable的同时也要实现IEnumerator接口。IEnumerator接口为: public interface IEnumerator { object Current { get(); } bool MoveNext(); void Reset(); }自定义集合
  • 327. foreach的本质foreach(ElementType element in Collection) {...} IEnumerator enumerator = ((IEnumerable)(collection)).GetEnumerator(); try { while(enumerator.MoveNext()) { ElementType element = (ElementType)enumerator.Current; {...} } }
  • 328. IEnumerable自定义集合示例using System; using System.Collections; //集合类的命名空间 namespace CustomCollection { // 定义集合中的元素MyClass类 class MyClass { public string Name; public int Age; // 带参构造器 public MyClass(string name,int age) { this.Name=name; this.Age=age; } } // 实现接口Ienumerator和IEnumerable类Iterator public class Iterator:IEnumerator,IEnumerable { // 初始化MyClass 类型的集合 private MyClass[] ClassArray; int Cnt; public Iterator() { // 使用带参构造器赋值 ClassArray = new MyClass[4]; ClassArray[0] = new MyClass("Kith",23); ClassArray[1] = new MyClass("Smith",30); ClassArray[2] = new MyClass("Geo",19); ClassArray[3] = new MyClass("Greg",14); Cnt = -1; } // 实现IEnumerator的Reset()方法 public void Reset() { // 指向第一个元素之前,Cnt为1,遍历是从0开始 Cnt = -1; } // 实现IEnumerator的MoveNext()方法 public bool MoveNext() { return (++ Cnt < ClassArray.Length); }
  • 329. // 实现IEnumerator的Current属性 public object Current { get { return ClassArray[Cnt]; } } // 实现IEnumerable的GetEnumerator()方法 public IEnumerator GetEnumerator() { return (IEnumerator)this; } static void Main() { Iterator It=new Iterator(); // 像数组一样遍历集合 foreach(MyClass MY in It) { Console.WriteLine("Name : "+MY.Name.ToString()); Console.WriteLine("Age : "+MY.Age.ToString()); } } } }
  • 330. 为了能够对数据项进行排序,就要确定两个数据项在列表中的相对顺序,也就是要确定两个对象的“大小”关系。一般来说,可以通过如下两种方式来定义大小关系。 第一种方式是针对对象本身。为了使对象自己能够执行比较操作,该对象必须实现IComparable接口,即至少具有一个CompareTo()成员。 System.IComparable接口中有如下方法: int CompareTo(object obj); 它根据当前对象与要比较的对象的“大小”返回一个正数、0或一个负数。 第二种方式是提供一个外部比较器,能够比较对象的大小,并实现IComparer接口。 System.Collections.IComparer接口中有如下方法: int Compare(object obj1, object obj2); 它根据第一个对象与第二个对象的“大小”返回一个正数、0或一个负数。 许多类在进行排序和查找时,都要求提供这样的外部比较器。IComparable与IComparer接口
  • 331. using System; public class Chair:IComparable { public double myPrice; public string myVendor, myID; public Chair() { } public Chair(double price, string vendor, string sku) { myPrice = price; myVendor = vendor; myID = sku; } int IComparable.CompareTo(Object obj) { if (obj is Chair) { Chair castObj = (Chair)obj; if (this.myPrice > castObj.myPrice) return 1; else if (this.myPrice < castObj.myPrice) return -1; else return 0; } throw new ArgumentException("object is not a Chair"); } }
  • 332. class MyApp { static void Main() { Chair[ ] chairs = new Chair[4]; chairs[0] = new Chair(150.0, "Lane", "99-88"); chairs[1] = new Chair(250.0, "Lane", "99-00"); chairs[2] = new Chair(100.0, "Lane", "98-88"); chairs[3] = new Chair(120.0, "Harris", "93-9"); Array.Sort(chairs); //默认只对基本类型排序 foreach (Chair c in chairs) { Console.WriteLine(c.myPrice + " " + c.myVendor + " " + c.myID); } } }
  • 333. 类型安全1MySchool中添加一个Teacher类Teacher jacky = new Teacher("成龙龙", 4); jacky.SayHi(); students.Add(jacky);能否加入一个Teacher对象?foreach (Object stuo in students) { Student stu = (Student)stuo; Console.WriteLine(stu.Name); }遍历这个集合是否有问题?演示:MySchool
  • 334. 类型安全2Student集合Scofield张靓靓周杰杰成龙龙添加对象遍历集合对象存储不易控制类型转换容易出错Teacher对象运行错误
  • 335. 什么是泛型集合泛型集合可以约束集合内的元素类型 编译时检查类型约束 无需装箱拆箱操作 加上using System.Collections.Generic; List,Dictionary 表示该泛型集合中的元素类型ListStudent对象Teacher对象允许添加不允许添加Student对象无需转换类型
  • 336. List students = new List(); 利用List存储班级集合List的使用students.Add(scofield); … students.Add(jacky);将Student对象加入班级将Teacher对象加入班级编译出错foreach (Student stu in students) { Console.WriteLine(stu.Name); }不需类型转换 遍历List集合演示:MySchool 只能保存Student对象
  • 337. 使用泛型集合ListStudent stu1 = students[2]; stu1.SayHi(); Students.RemoveAt(0); //List 方式 foreach (Student stu in students) { Console.WriteLine(stu.Name); } List的访问方式与ArrayList相同使用索引访问,无需类型转换利用索引删除遍历时不需要类型转换
  • 338. using System; using System.Collections.Generic; public class Example { public static void Main() { List dinosaurs = new List(); dinosaurs.Add("Pachycephalosaurus"); dinosaurs.Add("Amargasaurus"); dinosaurs.Add("Mamenchisaurus"); dinosaurs.Add("Deinonychus"); dinosaurs.Sort(); dinosaurs.Insert(0, "Tyrannosaurus"); foreach (string dinosaur in dinosaurs) { Console.WriteLine(dinosaur); } } }
  • 339. List 与 ArrayList通过索引删除元素添加对象方法相同通过索引访问集合的元素相同点需要装箱拆箱无需装箱拆箱可以增加任何类型增加元素时类型严格检查不同点ArrayListList异同点是否有哈希表那样存储Key和Value形式的泛型集合呢? 访问 List 与 ArrayList 的对比
  • 340. Dictionary概述Dictionary具有List相同的特性 约束集合中元素类型 编译时检查类型约束 无需装箱拆箱操作 与哈希表类似存储Key和Value的集合Dictionary students = new Dictionary();利用Dictionary存储学员集合Key存储String类型value存储Student类型
  • 341. Dictionary的使用students.Add(scofield.Name, scofield); … student stu2 = students["周杰杰"]; … students.Remove("周杰杰");添加一对Key/Value通过Key获取元素通过Key删除元素 //Dictionary 方式 foreach (Student student in students.Values) { Console.WriteLine(student.Name); }遍历ValuesDictionary的访问方式与哈希表相同演示:MySchool
  • 342. Dictionary与哈希表遍历方法相同添加对象方法相同通过Key获取Value相同点需要装箱拆箱无需装箱拆箱可以增加任何类型增加元素时类型严格检查不同点哈希表Dictionary异同点访问 Dictionary 与 哈希表 的对比
  • 343. 泛型的重要性泛型集合与传统集合相比类型更安全 泛型集合无需装箱拆箱操作 泛型的重要性 泛型是未来五年的主流技术之一 解决了很多需要繁琐操作的问题 提供了更好的类型安全性 CLR 支持泛型
  • 344. 第6章 C#文本处理和文件IO
  • 345. Unicode.NET默认使用Unicode编码的char char类型赋值 char k = ‘k’; char k1 = (char)75; char值转换为数值 char k=‘7’; int n=(int)k; n = (int)char.GetNumericValue(k); 字符及本地化System.Globalization char i = ‘i’; CultureInfo myCI=new CultureInfo(“az”, false); i =char.ToUpper(i,myCI);
  • 346. 字符及其Unicode分类:Unicode标准将Unicode字符分为30类。 UnicodeCategory枚举表示了这些类别。 char.GetUnicodeCategory() char k = 'k'; int iCat = (int)char.GetUnicodeCategory(k); //UppercaseLetter Console.WriteLine(char.GetUnicodeCategory(k)); char cr = '\n'; //Control Console.WriteLine(char.GetUnicodeCategory(cr));
  • 347. IsControl 指示指定的 Unicode 字符是否属于控制字符类别。 IsDigit 指示某个 Unicode 字符是否属于十进制数字类别。 IsHighSurrogate 指示指定的 Char 对象是否为高代理项。 IsLetter 指示某个 Unicode 字符是否属于字母类别。 IsLetterOrDigit 指示某个 Unicode 字符是属于字母类别还是属于十进制数字类别。 IsLower 指示某个 Unicode 字符是否属于小写字母类别。 IsLowSurrogate 指示指定的 Char 对象是否为低代理项。 IsNumber 指示某个 Unicode 字符是否属于数字类别。 IsPunctuation 指示某个 Unicode 字符是否属于标点符号类别。 IsSeparator 指示某个 Unicode 字符是否属于分隔符类别。 IsSurrogate 指示某个 Unicode 字符是否属于代理项字符类别。 IsSurrogatePair 指示两个指定的 Char 对象是否形成代理项对。 IsSymbol 指示某个 Unicode 字符是否属于符号字符类别。 IsUpper 指示某个 Unicode 字符是否属于大写字母类别。 Console.WriteLine(char.IsDigit(‘a’));
  • 348. String类创建字符串:拘留池(散列表),存储编译期间的所有字符串直接量。 string poem1=“Kubla Khan”; string poem2=“Kubla Khan”; string poem3=String.Copy(poem2); string poem4=“Christabel”;
  • 349. Console.WriteLine(poem1 == poem2); Console.WriteLine(poem1 == poem3); Console.WriteLine(ReferenceEquals(poem1, poem3)); Console.WriteLine(ReferenceEquals(poem1, poem2)); Console.WriteLine(ReferenceEquals(poem1, "Kubla Khan"));
  • 350. 比较字符串String.Compare int Compare(string str1, string str2); int Compare(string str1, string str2, bool IgnoreCase); int Compare(string str1, string str2, bool IgnoreCase, CultureInfo ci); int Compare(string str1, int index1, string str2, int index2, int len); str1==str2,返回0; str1>str2,返回>0; str1
  • 351. 比较字符串String.CompareOrdinal: Unicode码值为判断大小标准 string s1 = "AUTUMN"; string s2 = "autumn"; int result = String.Compare(s1, s2); //1 result = String.CompareOrdinal(s1, s2); //-32
  • 352. 搜索字符串[n] string poem = “Kubla Khan”; poem[0]; IndexOf, LastIndexOf(string, [int start], [int count]) int n=poem.IndexOf(“la”); n=poem.IndexOf(‘K’,4); IndexOfAny, LastIndexOf char[] chs = {‘a’, ‘e’, ‘i’, ‘o’, ‘u’}; N=poem.IndexOfAny(chs); N=poem.LastIndexOfAny(chs,2); 如果没找到,返回-1
  • 353. 字符串修改Insert(int, string) string str = "and he stoppeth three"; string verse = str.Insert(str.IndexOf(" three"), " one of"); PadRight, PadLeft string rem = "and so on"; rem = rem.PadRight(rem.Length + 3, '.'); Remove(P, n) Replace(A, B) string str = “nap ace sap path"; string verse = str.Replace(‘a’, ‘i’); verse = str.Replace(“a”, “i”);
  • 354. Split(char []) string str = “red, blue orange"; string[] split= str.Split(new char[] {‘ ’, ‘,’}); split[2]=? ToUpper() ToLower() Trim(), Trim(char []) string str = “ some"; str=str.Trim(); str=str.Trim(‘ ’, ‘s’); TrimEnd(char []), TrimStart(char []) string str = “ some"; str=str.TrimEnd(‘e’); SubString(n), SubString(n, l) ToCharArray(), ToCharArray(n,l) string str = “some"; char[] chs = str.ToCharArray();
  • 355. StringBuilder类字符串的主要缺点是:每次字符串变量的内容发生改变时,都必须重新分配内存。假设创建一个迭代100次的循环,每次迭代将一个字符连接到字符串,内存中将有100个字符串。 StringBuilder类通过分配一个工作区(缓存)解决这个问题。 using System.Text; StringBuilder操作 int i = 4; char[] ch = { 'w', 'h', 'i', 't', 'e' }; string myColor = " orange"; StringBuilder sb = new StringBuilder("red blue green"); sb.Insert(0, ch); sb.Insert(5, " "); sb.Remove(0, 6); sb.Append(myColor); sb.Replace("blue", "violet"); string colors = sb.ToString(); Console.WriteLine(sb);
  • 356. using System; using System.Text; public class MyApp { static void Main() { int start, end; string str = "a"; start = Environment.TickCount & Int32.MaxValue; for (int i = 0; i < 50000; i++) { str += "a"; } end = Environment.TickCount; Console.WriteLine(end - start); start = Environment.TickCount & Int32.MaxValue; StringBuilder sb = new StringBuilder(); for (int i = 0; i < 50000; i++) { sb.Append("a"); } end = Environment.TickCount; Console.WriteLine(end - start); } } 9359 16 除非应用大量文本处理,否则StringBuilder的优势并不明显,此时应使用标准连接操作符。
  • 357. 格式化数据String.FormatThere are 20 students with 75.00% passing
  • 358. 货币金额格式(C或c) 字符C(或c)用来将数据转换为货币金额格式。紧跟在字符C后面的数字定义货币金额数据小数点后应保留的位数。如: int CurValue=12345678; double fCurValue=12345678.125; String str1=String.Format(null,”{0:C2}”,CurValue); String str2=String.Format(null,”{0:C2}”,fCurValue); 结果: ¥12,345,678.00 ¥12,345,678.13
  • 359. 整数数据格式(D或d) xxxx 字符D(或d)用来将数据转换为十进制数整数格式。紧跟在字符D后面的数字则规定了数字将表示的位数。如果这个数字小于整数数据的实际位数,则显示所有的整数位,若这个数字大于整数数据的实际位数,则在整数数据的前面用数字0补足所有的位数。如: int CurValue=12345678; String str1=String.Format(null,”{0:D5}”,CurValue); String str2=String.Format(null,”{0:D9}”,CurValue); 结果: “12345678” “012345678”
  • 360. 科学计数法格式(E或e) x.xxxxE+xxx或x.xxxxE-xxx xxxxe+xxx或x.xxxxe-xxx 浮点数常数可以使用科学计数法表示(也叫指数形式) 字符E(或e)用来将数据转换为科学计数法形式。紧跟在字符E后面的数字规定小数点后应保留的位数,如果E后面没有数字,则小数点后保留6位(有效位数7位)。如: double fCurValue=12345678.125; String str1=String.Format(null,”{0:E6}”,fCurValue); String str2=String.Format(null,”{0:E}”,fCurValue); 结果: “1.234568E+007” “1.2345678E+007”
  • 361. 第4章 控制台输入和输出定点数据格式 xxxxxx.xx 字符F(或f)用来将浮点数据转换为定点数据格式。紧跟在字符F后面的数字规定小数点后面应保留的位数,如果指定的数字大于数据小数部分的位数,则在小数点的最后用数字0补足,如果F后面没有数字,则默认为两位小数。如: int CurValue=12345678; double fCurValue=12345678.125; String str1=String.Format(null,”{0:F2}”,CurValue); String str2=String.Format(null,”{0:F4}”,fCurValue); 结果: 12,345,678.00 12,345,678.1250
  • 362. 通用数据格式(G或g) 字符G(或g)用来表示通用数据格式。这个数据可能使用科学计数法来表示,也可能使用定点数据格式表示。在VisualC#中,若字符G后面没有数字,即没有规定浮点数的精度,则用定点数据格式;如果G后面有指定数字(精度),则用科学计数法表示。 double fCurValue=12345678.125; String str1=String.Format(null,”{0:G}”,fCurValue); String str2=String.Format(null,”{0:G4}”,fCurValue); 结果: 12345678.125 1.234E+07
  • 363. 自然数据格式(N或n ) xxx,xxx.xx 字符N(或n)用来表示自然数据格式。这种数据格式是将数据表示成“xxx,xxx.xx”,字符N后面的数字规定了数据格式中小数点后面的数字个数 int CurValue=12345678; double fCurValue=12345678.125; String str1=String.Format(null,”{0:N}”,CurValue); String str2=String.Format(null,”{0:N2}”,fCurValue); 结果: 12345678.00 12345678.13
  • 364. 十六进制数格式(X或x) 字符X(或x)用来表示十六进制数格式。这种数据格式是将整数转换成“xxxxxx”形式的十六进制整数,字符X后面的数字规定了格化数据的数字个数. int CurValue=123456; String str1=String.Format(null,”{0:X}”,CurValue); String str2=String.Format(null,”{0:X6}”,CurValue); 结果: 1e240 01e240
  • 365. 日期和时间System.DateTime日期和时间类(System.DateTime) C#将日期和时间数据封装在DateTime类中. 生成一个DateTime类的变量需要使用new运算符.也可以用下述方法: DateTime(int year,int month,int day); DateTime(int year,int month,int day,int hour, int minute,int second); DateTime(int year,int month,int day,int hour, int minute,int second,int millisecond);
  • 366. DateTime类的属性 Now 当前时间 Today 当前日期 Date 返回对象定义的日期 Day 返回对象定义的日(1-31) DayOfWeek 返回对象定义日期的星期数(1-7) DayOfYear 返回对象中的日在一年中的序号(1-365) Hour 返回对象定义的小时数值() Millisecond 毫秒数值 Minute 分钟数值 Month 月数值 Second 秒数值 Ticks 返回对象定义时间距离0001年1月1日12:00AM时刻的100倍纳秒数 TimeOfDay 时间值 Year 年数
  • 367. using System; public class MyApp { static void Main() { DateTime curDate = DateTime.Now; Console.WriteLine("Date: "+curDate); Console.WriteLine("Month: "+curDate.Month); Console.WriteLine("Day: "+curDate.Day); Console.WriteLine("DayOfWeek: "+curDate.DayOfWeek); Console.WriteLine("DayOfYear: "+curDate.DayOfYear); Console.WriteLine("Hour: "+curDate.Hour); } } Date: 2010-2-12 2:45:11 Month: 2 Day: 12 DayOfWeek: Friday DayOfYear: 43 Hour: 2
  • 368. 正则表达式 正则表达式提供了功能强大、灵活而又高效的方法来处理文本。 正则表达式的全面模式匹配表示法使您可以快速分析大量文本以找到特定的字符模式;提取、编辑、替换或删除文本子字符串;或将提取的字符串添加到集合以生成报告。 对于处理字符串的许多应用程序而言正则表达式是不可缺少的工具。
  • 369. 正则表达式简介 正则表达式: 用某种模式去匹配指定字符串的一种表示方式。 正则表达式由普通字符和元字符组成。 普通字符: 常使用的字符如字母、数字、汉字等元字符:可以匹配某些字符形式的具有特殊含义的字符,其作用类似于DOS命令使用的通配符。
  • 370. 正则表达式简介(续)1. 正则表达式基本书写符号 2. 正则表达式限定符 3. 匹配字符集 (1) 匹配字符集是预定义的用于正则表达式中的符号集。 (2) 如果字符串与字符集中的任何一个字符相匹配,它就会找到这个匹配项。 4. 分组构造
  • 371. 正则表达式简介5. 正则表达式举例 正整数: “ ^[0-9]*[1-9][0-9]*$” 非负整数:“^\d+$ ” 非正整数: “ ^((-\d+)|(0+))$” 整数: “ ^-?\d+$” 英文字符串: “ ^[A-Za-z]+$” 英文字符数字串: “ ^[A-Za-z0-9]+$” 英数字加下划线串: “^\w+$” E-mail地址:“^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$” URL:“^[a-zA-Z]+://(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\s*)?$”
  • 372. Regex类 Regex 类表示不可变(只读)正则表达式类。它还包含各种静态方法,允许在不显式创建其他类的实例的情况下使用其他正则表达式类。 Regex 类在System.Text.RegularExpressions命名空间下。 这里仅介绍IsMatch方法。 IsMatch方法:正则表达式在输入字符串中是否找到匹配项。
  • 373. 该方法有四种重载的形式: public bool IsMatch(string str); 表示在构造函数中指定的正则表达式在str中是否找到匹配项。 public bool IsMatch(string str, int start); 表示在构造函数中指定的正则表达式在str的指定起始位置开始是否找到匹配项。参数start表示开始搜索的字符位置。
  • 374. Regex类(续)public static bool IsMatch(string str, string pattern); 表示使用pattern参数中指定的正则表达式是否在str中找到匹配项。 public static bool IsMatch(string str, string pattern, RegexOptions options); 表示使用pattern参数中指定的正则表达式和options枚举提供的匹配选项在input中是否找到匹配项。其中options是RegexOption枚举值的按位“或”组合。
  • 375. Regex类(续)例: Regex r = new Regex(@"[0-9a-z]{3,5}"); string[] tests = {"abc", "123456", "(aa22bb33)", "ab"}; foreach (string test in tests) { if (r.IsMatch(test)) { Console.WriteLine("{0}中有匹配的项", test); } else { Console.WriteLine("{0}中没有匹配的项", test); } }结果: ab无匹配项,其他均有匹配项
  • 376. textBoxName图7-2 例7-2的设计界面textBoxAgetextBoxPasswordlabelNamelabelAgelabelPasswordbuttonOK
  • 377. using System.Text.RegularExpressions; …… //更改textBoxName.Text属性值触发的事件 private void textBoxName_TextChanged(object sender, EventArgs e) { labelName.Visible = !Regex.IsMatch(textBoxName.Text, "^[\u4e00-\u9fa5]{2,}$"); } //更改textBoxAge.Text属性值触发的事件 private void textBoxAge_TextChanged(object sender, EventArgs e) { labelAge.Visible = !Regex.IsMatch(textBoxAge.Text, @"^\d{1,3}$"); }
  • 378. //更改textBoxPassword.Text属性值触发的事件 private void textBoxPassword_TextChanged(object sender, EventArgs e) { labelPassword.Visible = !Regex.IsMatch(textBoxPassword.Text, @"^\w{5,}$"); } //单击【确定】按钮触发的事件 private void buttonOK_Click(object sender, EventArgs e) { if (labelName.Visible || labelAge.Visible || labelPassword.Visible) { MessageBox.Show("请按要求输入", "", MessageBoxButtons.OK, MessageBoxIcon.Hand); } else { MessageBox.Show("注册验证成功", "", MessageBoxButtons.OK, MessageBoxIcon.Warning); } }
  • 379. Match类 Match类表示单个正则表达式匹配操作的结果,得到的结果是只读的。该类没有公共构造函数,而是用Regex对象的Match方法返回的结果创建该类的对象。
  • 380. 例如: Regex r = new Regex("abc"); Match m = r.Match("123abc456"); if (m.Success) { Console.WriteLine("找到匹配位置:" + m.Index); Console.WriteLine("找到匹配结果:" + m.Value); } 运行结果: 找到匹配位置:3 找到匹配结果:abc
  • 381. MatchCollection类 MatchCollection类表示成功的非重叠匹配的序列,得到的集合是只读的。该类同样没有公共构造函数,而是用Regex.Matches方法返回的结果创建该类的对象。
  • 382. 例如: Regex r = new Regex("abc"); MatchCollection mc = r.Matches("123abc4abcd"); int count = mc.Count; String[] results = new String[count]; int[] matchPosition = new int[count]; for (int i = 0; i < count; i++) { results[i] = mc[i].Value; matchPosition[i] = mc[i].Index; Console.WriteLine("第{0}个匹配结果:{1},位置:{2}",i+1, results[i], matchPosition[i]); } 运行结果: 第1个匹配结果:abc,位置:3 第2个匹配结果:abc,位置:7
  • 383. Group类 Group类表示单个捕获组的结果。当与正则表达式匹配的子字符串有多组时,可以使用该类得到某一组的结果。例如: string ssn=“245-09-8444”; String pat = @“^(\d{3})-(\d{2})-(\d{4})$”;
  • 384. string text = "One car red car blue car"; string pat = @"(\w+)\s+(car)"; Regex r = new Regex(pat, RegexOptions.IgnoreCase); Match m = r.Match(text); int matchCount = 0; while (m.Success) { Console.WriteLine("Match" + (++matchCount)); for (int i = 1; i <= 2; i++) { Group g = m.Groups[i]; Console.WriteLine(string.Format("Group{0}='{1}'", i, g)); CaptureCollection cc = g.Captures;
  • 385. Group类(续) for (int j = 0; j < cc.Count; j++) { Capture c = cc[j]; Console.WriteLine(string.Format("Capture{0}='{1}', Position={2}", j, c, c.Index)); } } m = m.NextMatch(); } Console.ReadLine();
  • 386. Group类(续) 输出结果: Match1 Group1='One' Capture0='One', Position=0 Group2='car' Capture0='car', Position=4 Match2 Group1='red' Capture0='red', Position=8 Group2='car' Capture0='car', Position=12 Match3 Group1='blue' Capture0='blue', Position=16 Group2='car' Capture0='car', Position=21
  • 387. Match,GroupCollection和CaptureCollecture的关系string hexPatt = @"^(?[a-fA-F\d]{4})*";
  • 388. 反向引用组string speech = "Four score and and seven years"; pat = @"(\b[a-zA-Z]+\b)\s\1"; //pat = @"(?\b[a-zA-Z]+\b)\s\k< repeatand>"; MatchCollection mc = Regex.Matches(speech, pat); foreach (Match mt in mc) { Console.WriteLine(mt); //Console.WriteLine(mt.Groups[repeatand]); }
  • 389. System.IO:读写数据流的类
  • 390. Stream 类是派生出各种类的抽象类,处理字节流 其中的一些派生类包括 FileStream MemoryStream BufferedStream CryptoStream
  • 391. FileStream 类构造函数构造函数 FileStream(string FilePath, FileMode) FileStream(string FilePath, FileMode, FileAccess) FileStream(string FilePath, FileMode, FileAccess, FileShare)
  • 392. 与 FileStream 类一起使用的枚举数FileMode 枚举数 Append Create CreateNew Open OpenOrCreate TruncateFileAccess 枚举数 Read Write ReadWriteFileShare 枚举数 None Read Write ReadWrite
  • 393. using System; using System.IO; public class MyApp { static void Main() { try { FileStream fs = new FileStream(@"C:\artists\log.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite); byte[ ] alpha = new byte[6] { 65, 66, 67, 68, 69, 70 }; foreach (byte b in alpha) { fs.WriteByte(b); } fs.Position = 0; for (int i = 0; i < fs.Length(); i++) { Console.Write((char)fs.ReadByte()); } fs.Close(); } catch (Exception ex) { Console.Write(ex.Message); } } }
  • 394. MemoryStream 类用于从内存中读取数据和将数据写入内存中 以下是 MemoryStream 的一些方法方法 Read( ) ReadByte( ) Write( ) WriteByte( ) WriteTo( )
  • 395. using System; using System.IO; public class MyApp { static void Main() { try { FileStream fsIn = new FileStream(@"C:\manet.bmp", FileMode.Open, FileAccess.Read); FileStream fsOut = new FileStream(@"C:\manetcopy.bmp", FileMode.OpenOrCreate, FileAccess.Write); MemoryStream ms = new MemoryStream(); int imgByte; while ((imgByte = fsIn.ReadByte()) != -1) ms.WriteByte((byte)imgByte); ms.WriteTo(fsOut); byte[ ] imgArray = ms.ToArray(); fsIn.Close(); fsOut.Close(); ms.Close(); } catch (Exception ex) { Console.Write(ex.Message); } } }
  • 396. BufferedStream 类 2-1用于在缓冲区中读取和写入 当缓冲区满或关闭时,内容将刷新输出到底层流 它有两个重载的构造函数 语法
  • 397. using System; using System.IO; public class BuffStream { public void SaveStream() { Stream fsOut1 = new FileStream(@"captured.txt", FileMode.OpenOrCreate, FileAccess.Write); BufferedStream fileBuffer = new BufferedStream(fsOut1); byte[] buff; // array to hold bytes written to buffer bool readMore = true; while (readMore) { buff = FillBytes(); // Get array of bytes Console.WriteLine(buff[16]); for (int j = 0; j < buff[16]; j++) { fileBuffer.WriteByte(buff[j]); // store bytes in buffer } if (buff[16] < 16) readMore = false; // indicates no more data } fileBuffer.Close(); // flushes all remaining buffer content fsOut1.Close(); // Must close after bufferedstream }
  • 398. // method to simulate I/O device receiving data private static byte[] FillBytes() { Random rand = new Random(); byte[] r = new Byte[17]; // store random numbers to return in array for (int j = 0; j < 16; j++) { r[j] = (byte)rand.Next(); if (r[j] == 171) // Arbitrary end of stream value { r[16] = (byte)(j); // Number of bytes in array return r; } } r[16] = (byte)16; // entire buffer was filled System.Threading.Thread.Sleep(200); // Delay 500ms return r; } }
  • 399. public class MyApp { static void Main() { BuffStream bs = new BuffStream(); bs.SaveStream(); Console.WriteLine("Buffered data written."); Console.Read(); } }
  • 400. CryptoStream 类2-1用于链接数据流与加密对象,以便进行数据加密 位于 System.Security.Cryptography 命名空间中
  • 401. using System; using System.IO; using System.Security.Cryptography; // CryptoStream example public class MyApp {// Encrypt FileStream private static void WriteEncrypt(FileStream fs, string msg) { // (1) Create Data Encryption Standard (DES) object. DESCryptoServiceProvider crypt = new DESCryptoServiceProvider(); // (2) Create a key and Initialization Vector - requires 8 bytes crypt.Key = new byte[] { 71, 72, 83, 84, 85, 96, 97, 78 }; crypt.IV = new byte[] { 71, 72, 83, 84, 85, 96, 97, 78 }; // (3) Create CryptoStream stream object CryptoStream cs = new CryptoStream(fs, crypt.CreateEncryptor(), CryptoStreamMode.Write); // (4) Create StreamWriter using CryptoStream StreamWriter sw = new StreamWriter(cs); sw.Write(msg); sw.Close(); cs.Close(); }
  • 402. // Read and Decrypt a file stream. private static string ReadEncrypt(FileStream fs) { // (1) Create Data Encryption Standard (DES) object. DESCryptoServiceProvider crypt = new DESCryptoServiceProvider(); // (2) Create a key and Initialization Vector crypt.Key = new byte[] { 71, 72, 83, 84, 85, 96, 97, 78 }; crypt.IV = new byte[] { 71, 72, 83, 84, 85, 96, 97, 78 }; // (3) Create CryptoStream stream object CryptoStream cs = new CryptoStream(fs, crypt.CreateDecryptor(), CryptoStreamMode.Read); // (4) Create StreamReader using CryptoStream StreamReader sr = new StreamReader(cs); string msg = sr.ReadToEnd(); sr.Close(); cs.Close(); return msg; }
  • 403. static void Main() { // Open file to which encrypted string is written string toEncrypt = "Selected site is in Italy."; FileStream fs = null; try { fs = new FileStream(@"\corecsharp\data\testcrypt.txt", FileMode.Create, FileAccess.Write); MyApp.WriteEncrypt(fs, toEncrypt); fs = new FileStream(@"\corecsharp\data\testcrypt.txt", FileMode.Open, FileAccess.Read); string msg = MyApp.ReadEncrypt(fs); Console.WriteLine(msg); } catch (Exception ex) { Console.WriteLine(ex.Message); } fs.Close(); Console.Read(); }
  • 404. CryptoStream 类2-1用于链接数据流与加密对象,以便进行数据加密 位于 System.Security.Cryptography 命名空间中
  • 405. 读写文本文件可以用fileStream来读写文本文件,但是FileStream是通过字节形式来读写数据的,要把字节数据转换为文本,要自己处理编码转换。 对于文本文件的读写,通常用 StreamReader 类和 StreamWriter类更方便。其底层是通过FileStream实现读写文本文件的。
  • 406. 读取文本文件构造函数 Public StreamReader(string path, Encoding encoding); 其中path指定要读取的完整文件路径,encoding指定要使用的字节编码。例如GB2312,UTF8等编码形式
  • 407. using System; using System.IO; using System.Text; namespace ConsoleApplication2 { class Class1 { //多线程COM时使用[MTAThread] [STAThread] static void Main(string[] args) { StreamReader sr = new StreamReader("c:\\temp.txt", Encoding.GetEncoding("gb2312")); string line; while((line = sr.ReadLine()) != null) { Console.WriteLine(line); } sr.Close(); Console.ReadLine(); } } }
  • 408. 写入文本文件与StreamReader类对应的类是StreamWriter类,它专门用于写入文本文件 Public StreamWriter(string path,bool append, Encoding encoding); 其中path指定要写入的完整文件路径,append为false则该文件被改写,如果该文件存在,并且append为true,则数据被追加到该文件中。否则将创建新文件。Encoding指定要使用的字节编码。
  • 409. using System; using System.IO; using System.Text; namespace ConsoleApplication2 { class Class1 { [STAThread] static void main(string[] args) { StreamWriter sw = new StreamWriter("c:\\temp2.txt", true, Encoding.GetEncoding("gb2312")); string data = "hello world, how are you"; sw.WriteLine(data); sw.Close(); } } }
  • 410. 目录和文件
  • 411. 文件操作相关的类System.IO名字空间中包含了用于文件操作的相关类 File提供用于创建、复制、删除、移动和打开文件的静态方法,并协助创建FileStream对象 FileInfo提供用于创建、复制、删除、移动和打开文件的实例方法,并协助创建FileStream对象 Directory提供用于创建、移动和检索文件夹与子文件夹的静态方法 (Directory不可以被继承) DirectoryInfo提供用于创建、移动和检索文件夹与子文件夹的实例方法 Path提供了用于操作路径的静态方法。例如获得文件扩展名,从路径中获得文件名等
  • 412. 管理文件系统管理文件系统包括: 文件夹的管理 文件的管理 具体操作包括: 文件夹的创建、删除或移动 文件的创建、删除、复制
  • 413. Directory类和DirectoryInfo类1.Directory类 目录使用 Directory类,可以用目录类创建、移动目录,并可列举目录及子目录的内容。Directory类全部是静态方法。Directory类常用方法见表。
  • 414. Directory类的常用方法… … 方 法含 义示 例CreateDirectory 创建目录和子目录DirectoryInfodi=Directory. CreateDirectory(''c:\\mydir'');Delete删除目录及其内容Directory.Delete(''c:\\mydir'');Move移动文件和目录内容Directory.Move=(''c:\\mydir'', ''c:\\mynewdir'');Exists确定给定的目录字符串是否存在物理上对应的目录Directory.Exists(''c:\\mydir'');GetCurrentDirectory获取应用程序的当前工作目录console.WriteLine(''Current Directory is: ''+ currentPath); SetCurrentDirectory将应用程序的当前工作目录设置为指定目录Directory. SetCurrentDirectory(''c:\\ '');GetCreationTime获取目录创建的日期和时间DateTime dt = Directory.GetCreationTime (Environment.CurrentDirectory);GetDirectories获取指定目录中子目录的名称string [] subdirectoryEntries = Directory.GetDirectories(''c:\\mydir'');GetFiles获取指定目录中文件的名称string [] files= Directory. GetFiles (''c:\\mydir'');
  • 415. 2.DirectoryInfo类在使用DirectoryInfo类的属性和方法前必须先创建它的对象实例,在创建时需要指定该实例所对应的目录。例如: DirectoryInfo di=new DirectoryInfo(''c:\\mydir''); DirectoryInfo类的常用方法见表。DirectoryInfo类的常用方法方 法含 义示 例Create 创建目录di. Create();Delete删除DirectoryInfo实例所引用的目录及其内容di. Delete();MoveTo将DirectoryInfo实例及其内容移到新的路径di.MoveTo(''c:\\Program files'');CreateSubDirectory创建一个或多个子目录DirectoryInfo di = di.CreateSubdirectory("SubDir");GetDirectories返回当前目录的子目录DirectoryInfo[] subdirs=di. GetDirectories();GetFiles返回当前目录的文件列表FileInfo[] files=di. GetFiles();
  • 416. DirectoryInfo与Directory的区别Directory是一个实用类,它提供了文件夹操作的一般方法,每次调用这些方法,都需要通过参数来指定当前的操作是针对那个文件夹的(功能类似于API) DirectoryInfo类表示某一个指定的文件夹,需要在构造函数中绑定这个文件夹。它的属性与方法都是针对这个绑定的文件夹而言的。 一般而言,对于单一的操作使用Directory类更方便,针对某个特定文件夹的多项操作则使用DirectoryInfo类更方便。
  • 417. 常见文件夹操作获取当前应用程序的路径 Console.WriteLine(Directory.GetCurrentDirectory()); 获取当前计算机所有逻辑驱动器的名称 string[] drvs=Directory.GetLogicalDrives(); foreach(string drv in drvs) { Console.WriteLine(drv); } 新建文件夹 Directory.CreateDirectory(“c:\\mydirectory”); 删除文件夹 Directory.Delete(“C:\\mydirectory”,true); 移动文件夹 Directory.Move(“c:\\temp”,”d:\\qq\\temp”);
  • 418. 常见文件夹操作获取文件夹信息 DirectoryInfo dInfo=new DirectoryInfo (“c:\\Winnt”); Console.WriteLine(“创建时间:” + dInfo. CreationTime.ToLongTimeString()); Console.WriteLine(“最近访问时间”+dInfo.LastAccessTime.ToLongDateString()); Console.WriteLine(“上级文件夹”, dInfo.Parent.Name); Console.WriteLine(“有子文件夹”+dInfo.GetDirectories().Length.ToString()); Console.WriteLine(“包含文件”+dInfo.GetFiles().Length.ToString());
  • 419. 常见文件夹操作显示所有子文件夹 DirectoryInfo dInfo=new DirectoryInfo(“c:\\WinNT”); DirectoryInfo[] dirs = dInfo.GetDirectories(); foreach(DirectoryInfo dir in dirs) { Console.WriteLine(dir.Name); }
  • 420. 常见文件夹操作显示文件夹中的文件 DirectoryInfo dInfo=new DirectoryInfo(“c:\\WinNT”); FileInfo[] fs = dInfo.GetFiles(); foreach(FileInfo f in dirs) { Console.WriteLine(f.Name); }
  • 421. 常见文件夹操作显示文件夹中的文件(指定扩展名) DirectoryInfo dInfo=new DirectoryInfo(“c:\\WinNT”); FileInfo[] fs = dInfo.GetFiles(“*.exe”); foreach(FileInfo f in dirs) { Console.WriteLine(f.Name); }
  • 422. 文件管理File类与FileInfo类,与文件夹管理类似,File类是一个实用类,提供了操作文件的静态方法,FileInfo类代表某个具体的文件,提供了对该文件的属性和操作。
  • 423. 常见文件操作复制文件 File.Copy(“c:\\temp.txt”, “D:\\temp.txt”,true); 删除文件 File.Delete(“c:\\temp.txt”); 确定文件是否存在 bool b=File.Exists(“c:\\temp.txt”);
  • 424. 常见文件操作获取文件大小 FileInfo f=new FileInfo(“文件名”); long leng=f.Length; 获取文件的扩展名 FileInfo f=new FileInfo(“文件名”); string ext=f.Extension; string ext2=Path.GetExtension(“路径全名”); 获取路径中的文件名 FileInfo f=new FileInfo(“文件名”); string name=f.name string name22=Path.GetFileName(“路径全名”);
  • 425. 【例】 计算目录的大小。using System; using System.IO; public class ShowDirSize { public static long DirSize(DirectoryInfo d) { long Size = 0; // 累加当前目录下的文件大小 FileInfo[] fis = d.GetFiles(); foreach (FileInfo fi in fis) { Size += fi.Length; }
  • 426. //累加当前目录下的子目录大小 DirectoryInfo[] dis = d.GetDirectories(); foreach (DirectoryInfo di in dis) { Size += DirSize(di); } return (Size); } public static void Main(string[] args) { if (args.Length != 1) { Console.WriteLine("You must provide a directory argument at the command line."); } else { DirectoryInfo d = new DirectoryInfo(args[0]); Console.WriteLine("The size of \n{0} and its subdirectories \nis {1} bytes.", d, DirSize(d)); } } }
  • 427. 设置程序启动参数为C:\Inetpub,程序运行结果如下:
  • 428. Path类用来处理路径字符串,它的方法也全部是静态的。常用方法见表。方 法含 义示 例ChangExtension更改路径字符串的扩展名string newPath=Path. ChangExtension (''c:\\test.txt'', ''html''); Combine合并两个路径的字符串string newPath=Path.Combine (''c:\\ '', ''mydir'')GetDirectoryName返回指定路径字符串的目录信息string dir=Path. GetDirectoryName (''c:\\mydir\\test.txt'');GetExtension返回指定路径字符串的扩展名string ext=Path. GetExtension (''c:\\mydir\\test.txt'');GetFileName返回指定路径字符串的文件名和扩展名string name=Path. GetFileName (''c:\\mydir\\test.txt'');GetFileNameWithoutExtension返回不带扩展名的指定路径字符串的文件名(''c:\\mydir\\test.txt'');GetFullPath返回指定路径字符串的绝对路径string fullpath=Path. GetFullPath (''test.txt'');GetTempPath返回当前系统临时文件夹的路径string tempPath=Path. GetTempPath();HasExtension确定路径是否包括文件扩展名bool hasExt=HasExtension (''c:\\mydir\\test.txt'');Path类
  • 429. 【例】 演示 Path 类的主要成员。using System; using System.IO; class Test { public static void Main() { string path1 = @"c:\temp\MyTest.txt"; string path2 = @"c:\temp\MyTest"; string path3 = @"temp"; if (Path.HasExtension(path1)) { Console.WriteLine("{0} has an extension.", path1); } if (!Path.HasExtension(path2)) { Console.WriteLine("{0} has no extension.", path2); } if (!Path.IsPathRooted(path3)) { Console.WriteLine("The string {0} contains no root information.", path3); } Console.WriteLine("{0} is the location for temporary files.", Path.GetTempPath()); Console.WriteLine("{0} is a file available for use.", Path.GetTempFileName()); } }
  • 430. 运行结果如下: c:\temp\MyTest.txt has an extension. c:\temp\MyTest has no extension. The string temp contains no root information. C:\Documents and Settings\yangming\Local Settings\Temp\ is the location for temp orary files. C:\Documents and Settings\yangming\Local Settings\Temp\tmp55.tmp is a file available for use. 请按任意键继续. . .