中国海洋大学2023信息系统开发(.NET)复习
.NET平台特点
五个组成部分
- 底层操作系统
- .NET企业服务器
- Microsoft XML Web服务构件
- .NET框架
- 是.NET平台的一个和运行、执行环境
- 是一个多语言应用程序执行环境。主要有公共语言运行库和Framework类库组成
- .NET开发工具
包括集成开发环境Visual Studio.NET和.NET编程语言
C#基本语法
基本数据类型
数值类型
整数:sbyte、byte、short、ushort、int、uint、long、ulong
实数:decimal、float、double
字符类型:char,string
数值:整数,实数,十进制类型(Decimal),布尔类型
变量
-
命名规范
-
必须赋值后才能引用
命名规范
变量名开头只能是字母或下划线,禁用关键字,且不能含$
Camel命名:当使用一个或多个单词组成变量名是,第一个单词首字母小写,其他单词的首字母大写
Pascal命名:首字母大写。用于定义方法和类
类型转化
隐式转换
- 编译系统自动进行。只能从精度高的数据类型向精度低的数据类型进行转换
short s =1; int i = s
int i = 1; short s = i //错误,应该使用显式类型转换
显式转换
- 需要用户明确指定转换的目标类型 (类型说明符) (需要转换的表达式)
- 仅仅是为本次运算时所涉及的变量进行临时性转换
使用方法进行数据类型的转换
- Parse方法
Parse方法可以将特定格式的字符串转换为数值,其使用格式为:
数值类型名称.Parse(字符串表达式)
int i = int.Parse("100"); //字符串符合整形格式,转换成功
int j = int.Parse("100.0"); //字符串不符合整形格式,出错(会抛出异常)
- Conver类的方法
Convert类提供了常用的方法将数字字符串转化为相应的数值,如ToBoolean、ToByte、ToChar、ToInt32、ToSingle等方法;Convert类还提供了ToString方法将其他数据类型转换为字符串,提供了ToChar方法将整型的ASCII码值转换为对应字符
string s = "97";
int n = Convert.ToInt32(s); //n=97
char c = Convert.ToChar(n); //c=‘a’
//同样类型不能转换成功会抛出程序运行时异常
- ToString方法
将其他数据类型的变量值转换为字符串
int n = 97; string s = n.ToString(); //s="97"
string t = Convert.Tochar(n).ToString(); //t='a'
运算符和表达式类型
其他运算符与表达式
- typeof运算符——将c#中的数据类型转化为.NET框架中的类型
- new运算符——创建一个新的类型实例。 Form form = new Form();
- 条件操作符
? :——前真后假。如果有嵌套,则从右到左结合
引用类型
引用类型的数据存储在堆中
类
接口
interface Istudent{string answer();} //方法中不能够包含任何语句
委托
相当于指向函数的指针
数组
数组类型[] 数组名 = new 数组类型[数组长度]
int[] num1 = new int[10]{1,2,3}
int [,] numbers = new int[2,3]{{1,2,3},{4,5,6}} //2*3整形三位数组
字符串
集合
集合时通过高度结构化的方式存储任意对象的类。集合不仅能随意调整大小,而且对存储或检索存储在其中的对象提供了更高级的方法。集合也是一种引用类型。使用需要引用System.Collection命名空间
ArraylIst
大小可根据需要自动扩充。下限始终为0,且始终只是一维的
ArrayList list = new ArrayList();
list.add("abcdef");
list.add(30)
Queue
先进先出。插入或删除对象的时候,对象从队列的一端插入,从另一端溢出
Queue q1 = new Queue(50,2) //2为增长因子。队列容量不足时,队列长度调整为原来的2倍
q1.Enqueue("1") //在队尾添加1,每操作一次只能添加一个数据
Console.WriteLine(q1.Dequeue) //输出 1
Stack
先进后出。只能操作栈顶元素
Stack s1 = new Stack();
s1.push("beijing");
Cosole.Writeline(s1.pop());
Hashtable
表示键值对的集合。在保存集合元素的时候,首先根据键自动计算哈希代码,以确定该元素的保存位置,再把元素的值放入相应位置所指向的存储桶中。查找时,再次通过键所对应的哈希代码到特定存储桶中搜索,这样可以极大的提高查找一个元素的效率
Hashtable h1 = new Hashtable(100,2);
h1.Add(1001,"林白军");
Console.WriteLIne(h1[1001]);
h1.Remove(1001);
SortedList
键值对的集合,适合对一列键值对进行排序。实际上时Hashtable和Array的集合。
List
列表是指一系列元素的组合,列表中可以有重复的元素
list<T>泛型表示可通过索引访问的强类型对象列表
Dictionary
字典也是可作泛型类
Dictionary<TKey,TValue>提供了从一组键到一组值得映射
值类型
枚举
enum orientation{north=6,south,east=10,west}; //south=7, west=11
int x = (int)orientation.south
装箱与拆箱
装箱是指将一个值类型变量转换为一个引用类型的变量。这个过程式隐式转换过程。
1. 创建一个引用类型的实例
1. 将值类型变量的内容复制给该引用类型实例
在.NET中,Object类时所有类型的基类。所以,装箱意味着把一个值类型的数据转换为一个对象(Object)类型的数据
int i= 123;
object boxing = i;
拆箱是将一个引用类型显式地转换成一个值类型。这个过程必须式显式转换
程序设计
赋值语句——单赋值、连续赋值
输入输出
- 标准输入输出
Read与ReadLine。Read读取一个字符,返回值是int类型;ReadLine读取一行字符,返回值是srting类型
input:a
int i = Console.Read(); //i=97
char c = (char)Console.Read(); //i=a
Write与WriteLine。
Console.Write("请输入一个整数:");
int j = int.Parse(Console.ReadLine());
Console.WriteLine("输入的整数为:"+j);
int i = 5; string s = "five";
Console.Write("i={0},s={1}", i, s);//数字从0开始,一次对应表达式列表中的表达式
- 消息框
后续可以依据result进行处理
DialogResult result = MessageBox.Show(
"这是消息内容", // 消息内容
"消息标题", // 标题
MessageBoxButtons.YesNoCancel, // 按钮类型
MessageBoxIcon.Question, // 图标类型
MessageBoxDefaultButton.Button1, // 默认按钮
MessageBoxOptions.DefaultDesktopOnly // 其他选项
);
窗体
关闭窗体和隐藏窗体的区别
关闭窗体将窗体彻底销毁,之后无法对窗体进行任何操作
隐藏窗体只是将窗体不显示。可以使用Show或ShowDialog方法使窗体重新显示
模式窗体与非模式窗体的区别
模式窗体在其关闭或隐藏前无法切换到该应用程序的其他窗体
非模式窗体可以在窗体之间任意切换
组件与控件
组件
组件是一种类。组件有可视化界面组件,也有非可视化界面组件。•标签、文本框、按钮控件被放在窗体上,因而是可视化组件;而图像列表、计时器被放在窗体下方的组件区,因而是非可视化组件。
PictureBox(通常视作控件)
- 加载图像——设置Image属性
//设置Image属性
Image = Image.FromRile(@"文件路径");
Image = new Bitmap(@"文件路径");
//加载图像
图片框名.Load (@"文件路径或URL ");
图片框名.LoadAsync(@"文件路径或URL ");
//@“d:\My Documents\Images\ Kiya.jpg”等价于"d:\\My Documents\\Images\\Kiya.jpg"
ImageList
可以通过索引值或键值来访问图像
//获取第三个图像(如果名字为pic3.jpg)
imageList1.Images[2] 或 imageList1.Images[“pic3.jpg”]
要获取其键值,可以使用 imageList1.Images.Keys[2]
要获取其索引值可以使用 imageList1.Images.IndexOfKey("pic3.jpg ")
Timer——计时器
按照一定时间间隔,周期性地自动触发事件地控件
常用属性:Interval(间隔,以毫秒为基本单位)
常用方法:Start,Stop
常用事件:Tick
控件
控件时提供或实现用户界面功能的组件。每个控件都是组件,单并不是么个组件都是控件。习惯上,将可视化的组件称为控件,将非可视化的组件仍成为组件。
单选按钮与复选框
RadioButton——单选按钮
使用Tab键将焦点移动到一组单选按钮后,再用方向键从组中选定一个按钮。
常用属性:AutoCheck(单击时是否自动更改状态),CheckAligh,Checked(获取是否选中,默认值为false,即未选中)
CheckBox——复选框
使用Tab键将焦点移动到选项组之后,用方向键再各个复选框之间移动焦点,用空格间切换复选框的选择状态
常用属性
Checkstate属性:单选按钮只有未选中和选中两种状态。而复选框则有未选中、选中、不确定三种状态。即该属性有三个取值:Unchecked、Checked和Indeterminate。
Checked属性:但通过CheckState属性的值为Unchecked时,Checked属性的值自动变为false;当设置CheckState的值为Checked或Indeterminate时,Checked属性的值自动变为true。
容器控件: GroupBox, Panel,TabControl
只有GroupBox控件可以显示标题
只有Panel控件可以有滚动条
TabControl控件用与显示多个选项卡页
Listbox——列表框
listBox1.Items.Add(0);
listBox1.Items.Add("zero");
object[] obj = new object[4]{2,"two",3,"three"};
listBox1.Items.AddRange(obj);
listBox1.Items.Insert(2, 1); //在指定索引处插入一个项目。前面的数字是索引
listBox1.Items.Insert(3, "one");
//此时列表为:0,zero,1,one,2,two,3,three。
//如果进行排序(Sorted=true),则为0,1,2,3,one,three,two,zero。
listBox1.Items. Remove (0);
listBox1.Items. RemoveAt (0);
//listBox1中的列表为:1,one,2,two,3,three。
CheckedListBox——复选列表框
checkedListBox1.Items.Add(1);
checkedListBox1.Items.Add("a",true);
checkedListBox1.Items.Add(2,CheckState.Indeterminate);
Combox——组合框
SelectedItem属性来获取当前选定项,如果未从列表中选择,该属性值为null。所以,也可以利用ComboBox控件的SelectedItem属性来判断当前内容是从列表中选择的,还是从文本框中输入的。
ProgressBar——进度条、
常用属性:value(攀升的界限),Minimuim(下限,一般为0),Maximum(上限)
常用方法:PerformStep(按照Step属性的数量来增加进度条的当前位置),Increment(int value)(按照指定的数量来增加进度条的当前位置)
面对对象的程序设计1
类和对象概述
每一个有明确意义和边界的事物都可以看作是一个对象。任何一个对象都具有属性和方法两个要素。消息是对象与对象之间用来沟通的信号、语言等。专门用来 护理对象与对象之间传递消息的方法称为事件。
类具有相同对象的集合
主要特性
封装性
- 将方法和数据合并到一个类中
- 控制方法和数据的可访问性
继承性
真正目的是可重用性
多态性
多态性实际上是指呈现多种形态或形式的能力
经常使用重载方法和重写方法来实现多态性
类
类成员的可访问性
| 访问修饰符 | 性质 |
|---|---|
| public | 公共的,完全公开没有限制的 |
| internal | 内部的,在同一个命名空间内可以访问(只能在当前程序集合中访问) |
| private | 私有的,仅在类的内部可以访问 |
| protected | 受保护的,类的内部和继承子类可以访问 |
| protected internal | 访问权限仅限于当前程序集或该类的派生类 |
对象
创建:只能用new
数据成员
常量
[访问修饰符] const 数据类型 常量名= 常量的值;
当一个常量作为类的成员存在时,这个常量就隐含地成为类地静态成员。因此要通过类名对他进行访问。const和static不能同时存在
字段
数据类型可以是引用类型或值类型。
声明字段时,一般要指定相应的访问权限,其默认的访问权限是private(注意和类的默认访问权限区分开)。还可以在字段声明前加上关键字static或readonly,使其成为静态字段或只读字段
方法成员
访问权限:private、internal、protected、protected internal,默认是private
方法的参数
形参:声明方法时,所定义的参数是形式参数。
实参:调用方传递的是实际数据,称为实参。
值类型参数
定义:在声明变量是不论是否赋值,编译器都会为其分配内存空间
值类型:byte,short,int,long,float,double,decimal,char,enum,struct...
基类:派生于System.Value
传参:被调用的方法所接收到的只是实参数据值的一个副本。方法内部改变形参不影响实参。是一种单向值传递。
引用型参数(ref)
定义:在声明或定义一个类时,只在栈中分配一块小的内存(4字节)用于存放地址。此时并没有为分配对上面的内存空间。当使用new关键词创建(实例化)时才会分配堆上的空间,并把对空间的地址保存在那块小的内存中
引用类型:object,string,Array(int[] arges),class,interface,delegate
基类:派生于Object
传参:把实参变量的引用(内存地址)赋给对应的形参变量。方法内部改变的数据值印象实参
- 使用return语句一次只能返回一个数据,如果需要返回多个数据,可以利用引用型参数实现
- c#通过ref关键字来声明引用参数,无论是形参还是实参,只希望传递数据的引用,就必须添加ref关键字
public void Swap(ref int x,ref int y){...}
Swap(ref a,ref b);
输出参数(out)
方法中的return语句只能返回一个数据,虽然也可以使用引用型参数返回多个数据,但要求先初始化实参。而输出型参数不需要堆实参进行初始化,它专门用于把方法中的数据通过形参返回给实参,但不会把实参的值传递给形参。一个方法中允许有多个输出型参数,
C#通过out关键字来声明输出型参数,无论是形参还是实参,只要是输出型参数,都必须添加out 关键字。
string path = Console.ReadLine();
string dir,file;
SplitPath(path,out dir,out file); //实参接受形参的输出。方法中只需要对dir和file赋值即可。
数组型参数(params)
形参数组前不添加params修饰符:所对应的实参必须是一个数组名
形参数组前添加paras修饰符:所对应的实参可以是数组名,也可以是数组元素值的列表
规则:
- 只允许定义一个数组型参数,并且必须位于参数列表的最后
- 数组型参数所定义的数组必须是一维的
- 数组型参数不能同时作为引用型参数或输出型参数
方法的重载(overload)
定义:在同一个类中定义相同名称的多个方法成员的能力。重载的成员之间的唯一的差别,就是它们具有不同的签名(不同的参数类型或不同的参数个数)
当函数解析返回两个或多个方法时,调用被认为是不明确的,会出现错误;反过来,如果没有方法返回,也会出现错误
构造函数
默认构造函数没有参数。 构造函数可以重载
[访问修饰符] 类名(参数列表)
{
[ 语句; ]
}
析构函数
不能带任何参数,也不能返回任何值,不能是静态的,也不能有访问修饰符。不能是继承而来的,也不能显式地调用析构函数。
由.NET Framework决定何时回收对象占用的资源。
~类名( )
{
[ 语句; ]
}
属性
属性是对类地字段提供特定访问地类成员
[访问修饰符] 数据类型 属性名
{
get
{
// 执行代码
return <表达式>;
}
set
{
// 执行代码
// 表达式 (可以使用关键字value)
}
}
- 一个属性包含一个get方法和一个set方法。根据属性的上下文,隐式调用正确的方法。get访问器获得属性值(右值)并将它返回给调用的程序,set访问器用设置或修改属性值(左值)
- 可访问性和仍和包含在属性头的修饰符都应用于get和set方法
- 在set方法中,value代表隐式的参数。
- 读写性质:同时包含了get和set;只读性质:只包含有get访问器;只写性质:只包含有set访问行
索引器
[访问修饰符] 数据类型 this [索引类型 形参]
{
get
{
// 执行代码
return <表达式>;
}
set
{
// 执行代码
// 表达式(可以使用关键字value)
}
}
- 访问修饰符包括public、protected、internal、new、virtual、sealed、override、abstract、extern;
- 数据类型表示将要存取的数组或集合元素的类型
- this表示操作本对象的数组或集合成员。可以简单认是索引器的名字。所以索引器不能自定义名称
- 索引类型表示该索引器使用哪一种类型的索引来存取数组或集合元素,可以是整型,也可以是字符串类型。
多索引
使用不同的前面来唯一标识每个索引
多索引也叫索引的重载
静态类(static)
特征:
- 只包含静态成员
- 不能被实例化
- 是密封的
- 不能包含实例构造函数,但可以声明静态构造函数,以分配初始值或设置某个状态
- 不可继承
静态成员:
- 使用static修饰符
- 不能包含virtual、abstract、override等其他修饰符
静态构造函数:
- 不能显示调用
- 不用定义访问修饰符(也就是说只有static修饰符)
- 没有参数
- 不能被继承或重载
- 一个静态类只能有一个静态构造函数
常用.NET框架类型
Object类
.NET类库中最顶层的基类,提供了四种公有成员方法
- string ToString():获得对象的字符串表示
- Type GetType():获得对象的数据类型
- bool Equals(Object obj):判断当前对象于obj对象是否相等
- int GetHashCode():获取对象的哈希函数值
Convert类,Math类,DataTime类
面对对象的程序设计2
继承
继承的实现
基类型(父类)——被继承的类
派生类型(子类)——继承后产生的类
[访问修饰符] class 类名 :基类列表
{
类的成员
}
一个类只能对单个类进行继承,但能继承多个接口
基类的可访问性要大于派生类
internal class Aclass{ …… }
public class Bclass:Aclass{ …… }
//这样会产生一个编译错误
隐藏基类成员
如果在派生类种定义一个于基类同名的方法,需要让派生类种的方法隐藏基类种的方法,就应该使用new关键字显式声明
public class B: A
{//使用new关键字隐藏基类同名方法
public void new Method()
{
Console. WriteLine (“method() in B”);
}
}
- 成员隐藏并不局限于方法成员,也可以用于数据成员和内数据成员。并且也不需要基类中的成员式虚拟的(virtual)
- 通常用于改写基类中的非虚拟方法
base关键字
base关键字用来表示基类,通过它可以访问基类的成员。
在派生类中,如果使用new隐藏某个基类成员,派生类中直接访问的就是它自己定义的成员。如果需要调用基类中被隐藏的成员,就必须使用关键字base。(p175)
派生类的构造函数
派生类构造函数名([参数列表1]):base([参数列表2]);
Public class Person{
public Person(string name,char sex){
this.name=name;this.sex=sex;
}
}
Public class Student:Person{
Public Student(string name,char sex,string school):base(name,sex){
this.school = school;
}
}
参数列表2必须与希望调用的基类构造函数匹配,如果参数列表2为空,则表示调用基类的默认构造函数;构造函数不能直接利用其函数名调用,否则,会引起编译错误。
多态性
重载和重写
方法重载是指指程序在同一个类中定义相同名称的多个方法成员的能力
真正和多态相关的是重写。方法重写在继承时发生,是指在派生类中重新定义基类的方法。派生类中方法于基类中方法的名称、返回值类型、参数列表和访问权限都是相同的。当派生类重新定义了基类的虚拟方法后,基类根据赋给它的不同的派生类引用,动态地调用属于派生类地对应方法。这样的方法调用其方法地址是在运行期动态绑定的。
c#中,经常在派生类中通过基类的虚方法或实现抽象方法来实现多态
虚方法
C#中,基类成员的改写有重写和隐藏两种方式
上一节中使用new关键字实现基类成员的隐藏
在C#中,只有虚拟方法成员(包括方法、属性、索引器和事件)可以重写(也叫覆盖)。在基类中,需要使用关键字virtual将某个方法显式声明为虚拟方法,然后再派生类中必须使用关键字override显式声明一个方法以重写某个虚拟方法。方法重写时,必须注意派生类中的方法应该于基类中被重写的方法完全相同。
- 不能重写非虚拟方法
- virtual不能和static、abstract、override修饰符一起使用
- 派生类对象即使被强制转换成基类对象,所引用的仍然是派生类的成员
- 派生类可以通过密封来停止虚拟继承,此时派生类成员使用sealed override来声明
public class Automobile{
public virtual void Makesound(){console.WriteLine("汽车鸣笛");}
}
public class Bus:Automobile{
public override void Makesound(){console.WriteLine("公交车鸣笛");}
}
......
Automobile[] autos = new Automobiles[2];
autos[1] = new Automonile();
autos[2] = new Bus();
foreach (Automobiles a int autos){
a.Makesound(); //会调用派生类中的方法
}
抽象方法和抽象类
抽象方法
抽象方法是一种虚拟方法(但不能用关键字virtual显式声明),可以被重写以实现多态。
abstract可以修饰方法、属性、索引器和事件
抽象方法是一个不完全的方法,它只有方法头,没有具体的方法体。
抽象方法为隐式的虚方法,所以为继承的抽象类提供实现代码的方式于重写一个虚方法相似,也要使用override关键字
abstract void Method();
abstract int Weigh{get;set;}
抽象类
如果某个类中含有抽象方法(或抽象类成员),那么这个类就是一个抽象类,抽象类必须使用abstract修饰。抽象类是包含一个或多个抽象类成员的类,本身必须声明为abstract,但抽象类可以包含非抽象的成员。
抽象类中的方法和属性,只有声明(使用关键字abstract),没有实现部分
抽象类永远也不能实例化。
抽象类可以位于类层次的结构的上层,确定了派生类的基本结构和意义,使得程序框架更容易建立
public abstract class Person{
public abstract int Score{get;set;}
}
public abstract class Student:Person{
public override int Score{
get{return score;}
set{score = (value>0?)value:0;}
}
}
Vitrual VS Abstract
- 当方法前使用了virtual关键字时,它就成为虚拟方法;虚拟方法是多态的基础,在派生类中能够(但不是必须)改变方法的执行,改变基类中虚方法的过程叫重写或覆盖。
- 当方法前使用了abstract关键字时,它就成为抽象方法;抽象方法是隐含的虚拟,而且必须被派生类实现重写。
- 抽象方法与虚拟方法的差别在于:虚拟方法有实现,抽象方法没有实现。
密封方法和密封类
密封方法
使用关键字sealed修饰的方法是密封方法(包括方法、属性、索引器和事件),表示不能再继承子类中覆盖该方法。
class A
{ public virtual void Print() { …… } }
class B:A
{ sealed override void Print() { …… } }
class C:B
{
override void Print(){ …… } //错误
}
密封类
该类不能被其他类继承(不能有派生类)
既然密封类不能被继承,因此,其中的方法不能声明为虚拟的或是抽象的
public abstract class Shape
{ pulic abstract double GetArea(); }
public sealed class Circle : Shape
{ …… }
- 将某个方法声明为密封的是为了说明该方法不能被重写,因此密封方法应该同时是一个重写方法,关键字sealed必须和override在方法声明中一起使用
- 抽象类不能声明为密封的
接口
接口是比抽象类型更为“抽象”的一种数据类型。它所考虑的是“能提供什么服务”,而不考虑于实现有关的任何因素。
接口主要定义一个规则,让企业内部或行业内部的软件开发人员按标准去实现应用程序的功能。因此,必须由类或结构来继承所定义的接口并实现它,否则定义接口就毫无意义
每个派生类只允许一个基类,即C#语言只支持单继承。接口可以间接实现多继承
定义
接口是C#的一种数据类型,属于引用类型。接口像一个抽象类,可以定义方法成员、属性成员、索引器和事件等,单接口不提供对成员的实现,而继承接口的类则必须提供接口成员的实现。
/*public*/ interface Iperson{ //所有接口成员隐式地具有public访问修饰符
string Name{get;set;}
char Sex{get;set;}
string Show();
}
接口成员可以是属性、方法、索引器和事件。但不能是字段、常量、构造函数、析构函数、运算符和内部数据类型,而且不能包含静态成员。
当派生接口要隐藏从基接口继承来地成员地时候,应使用new关键字把它标识为重新声明地成员
实现
继承接口的类分为两类
- 如果支持接口的类是非抽象的,那么它必须为接口中的所有方法提供实现
- 如果支持接口的类是抽象的,那么它必须支持接口中的所有方法,且这些方法要么提供具体实现,要么是抽象的。
接口中的方法都是公共的,因此实现的时候必须用修饰符public修饰,否则编译出错
- 与抽象方法不同,接口中的方法并不是虚拟方法,实现时不能使用关键字override,但同样具有多态性
使用(p191)
实现接口的方式主要由两种:隐式实现和显式实现。前面介绍的主要是隐式实现接口成员的方式,类似于由基类直接产生派生类的方式。而现实实现接口成员的方式主要用于有多个接口继承并且两个或多个接口使用了同一个名称声明成员的场合
在实现时,为了区分是从哪个接口继承,避免产生二义性,c#建议使用显式实现接口的方式
即 接口名.方法名
public interface Ishape{
double GetArea();
double GramLength();
}
public interface IshapShow{
string ShowMe();
}
//隐式调用
public class Circle:Ishape, IshapShow{
public double GetArea(){
return Math.PI*r*r;
}
poublic string ShowMe(){
return $"Area = {GetArea().ToString()}";
}
}
//显式调用
public class Circle:Ishape,IshapShow{
double Ishape.GetArea(){ //显式实现的成员不能带任何访问修饰符
return Math.PI*r*r;
}
string IshapShow.ShowMe(){
return "Area = "
}
}
//使用隐式实现
Circle myCircle = new Circle(4);
IShape[] myShapes = {myCircle};
Console.WriteLine(myCircle.Showme());
//Console.WriteLIne(myShapes[0].Showme()) 这个会报错
//使用显式实现
//double myArea = myCircle.GetArea(); 这样会报错。显式实现必须通过所属的接口来引用
Ishape myICircle = myCircle;
double myArea = myICircle.GetArea();
分布类(partial class)
- 同一分布类的所有部分应该具有相同的可访问性
- 同意分布类的所有部分的定义都必须使用partial进行修饰,而且partial修饰符只能出现在靠关键字class、struct或interface前面的位置
- 分布类中允许嵌套
委托
委托能够将方法本身作为参数进行传递。
委托是一种特殊的引用类型。
C#允许把任何具有相同签名(相同的返回值类型和参数)的方法分配给委托变量
委托的使用过程分为三部:类型定义,对象创建和方法绑定,方法调用
声明
[访问修饰符] delegate 返回值类型 委托名([参数列表] )
public delegate int Calculate( int x,int y)
c#中定义的所有委托类型,实际上都是.NET类库中Delegate类的派生类,但Delegate是个抽象类,不能创建实例。
使用delegate关键字来创建的委托类型是密封的,因此也不能从自定义的委托派生
实例化
委托类型 委托变量名 = new 委托型构造函数(委托要引用的方法名);
int intAdd(int x,int y){ return x+y; }
Calculate a1 = new Calculate(intAdd);
通过委托调用方法
Calculate cal = intAdd;
int result = cal(6.8);
使用匿名方法
c#允许不写出该方法的定义,而是将该方法的执行代码直接写在委托对象的创建表达式中。此时,被封装的方法叫做匿名方法。
委托类型 委托变量名 = delegate ([参数列表]) {代码块};
Calculate cal = delegate(int x,int y){return x*y;}
匿名方法不是没有名称,而是指开发人员无需再源代码中为其命名
匿名方法的参数名不能和已有的外部变量名相同。如果相同,则代表同一个变量,此时称外部变量被匿名方法所捕获。
多路广播与委托合并
每次委托调用的都是一个方法,这种只引用一个方法的委托称为单路广播。
当委托添加更多指向其他方法的引用时,这些引用被存储再委托的调用列表中,这种委托就是多路广播委托
可以通过调用多路广播委托一次性调用所有的方法,这一过程称为多路广播
Calculate cal1 = new Calculate(Mul);
Calculate cal2 = new Calculate(Div);
Calculate cal3 = cal1 + cal2;
cal1 += Div
对一个空的委托对象进行方法调用会引发程序异常 ,所以安全的做法时先判断该委托对象是否为空。
if(cal3 != null) {……}
协变和逆变
协变:允许将带有派生返回类型的方法作为委托
逆变:允许将带有派生参数的方法作为委托
事件(p205)
事件是类用来通知对象需要执行某种操作的方式
事件定义为对象发送的消息,以发消息的形式通知操作的发生。事件本质上就是对消息的封装,用作对象之间的通信
一个完整的事件处理系统一般有三个构成要素
- 事件源:指能触发事件的对象,也称为事件的发送者或事件的发布者
- 侦听器:指能接收到事件消息的对象,Windows提供了基础的事件侦听服务
- 事件处理程序:当事件发生时,可以对事件进行处理,也称为事件函数或事件方法
实现步骤
- 定义委托类型,并再发布者类中定义以一个该类型的公有事件成员
- 在订阅者类中定义委托处理方法
- 订阅者对象将其事件处理方法合并到发布者对象的事件成员上
- 发布者对象在特定的情况下激发事件,自动调用订阅者对象的委托处理方法
声明事件和触发事件
事件实际上是委托类型的变量,是委托的一种特殊形式。C#允许使用内置的委托类型来声明一个标准的事件,也允许自定义委托,再声明自定义事件。
在.NET Framework中内置的EventHandler委托具有两个参数,它们的数据类型分别是Object和EventArgs类型,并且没有返回值。Object类型的参数名通常为sender,表示事件发布者本身;EventArgs类型的参数名通常为e,它将System.EventArgs类的新实例传递给事件处理方法,主要包括与事件相关的数据。
使用内置委托声明事件的一般格式如下
public event EventHandler onClick
使用自定义委托声明事件一般格式如下
public delegate void LightEventHandler(bool color);
public event LightEventHandler OnColorChange;
订阅事件和处理事件
事件声明后,得到一个委托型的变量,并不意味着能够成功触发事件。如何将声明的事件和事件处理程序联系起来呢?这就需要用到订阅类(负责绑定事件与事件方法的类)
也就是说需要在接受事件的类中创建一个方法来响应这个事件(即处理事件),并且创建委托对象把事件与事件方法联系起来,这个操作过程称为“订阅事件”
namespace Event
{
public delegate void LightEventHandler(bool isRed);
public class TrafficLight { //事件发布类
private bool isRed{get;set;}
public event LightEventHandler OnColorChange; //声明事件
public void ChangeColor(){
isRed = !isRed;
Console.WriteLine(isRed ? "red":"green");
if(OnColorChange!=null){
OnColorChange(isRed) //触发事件
}
}
}
public class Car{
private bool bRun = true;
public void Enter(TrafficLight light){ //订阅事件
light.OnColorChanged +=LightColorChange;
}
public void Leave(TrafficLight light){ //取消事件
ligh.OnColorChanged -= LightColorChange;
}
public void LightColorChange(bool isRed){ //处理事件
if(bRun && isRed){bRun = false;}
if(!bRun && !isRed){bRun = true;}
}
}
class Program{
static void Main(string args[]){
TrafficLight light = new TrafficLight();
Car c = new Car();
car.Enter();
light.ChangeColor();
car.leave();
}
}
}
注意,委托,事件,处理事件的函数的形参列表相同
泛型
泛型与普通类的区别是多了一个或多个表示类型的占位符,这些占位符用尖括号括起来,例如
public class Myclass<T>
public interface ISession<TSession>
public class Search
{
public static int SeqSearch<TData>(TData[] items, TData item)
{
for (int i = 0; i < items.Length; i++)
{
if (items[i].Equals(item))
return i;
}
return -1;
}
}
}
}
class Example4_14
{
static void Main(string[] args)
{
int[] score = new int[] { 56, 67, 32, 48, 96, 80 };
string[] name = new string[] { "Tom", "John", "Mike", "Dave", "Steave", "Loni" };
int i = 0;
i = Search.SeqSearch<int>(score, 96);
Console.WriteLine("96在score中的位置:"+i.ToString());
i = Search.SeqSearch<string>(name, "Mike");
Console.WriteLine("Mike在name中的位置:" + i.ToString());
Console.Read();
}
}
异常
异常分为三类
- 代码编写时出现得错误——语法错误
- 程序算法得错误——逻辑错误
- 在应用程序运行时产生得错误——运行时错误
异常处理
Exception类是SystemException和ApplicationException两个泛型子类的基类,所有的异常对象都直接继承自这两个子类。
SystemException表示系统引发的异常,ApplicationException表示编程人员在程序中所引发的异常
| 异 常 类 | 描 述 |
|---|---|
| System.ArithmeticException | 算术运算时发生异常 |
| System.ArrayTypeMismatchException | 当存储一个数组时,被存储的元素与数组的实际类型不兼容而导致存储失败,引发此异常 |
| System.DivideByZeroException | 在试图用零除整数值时引发 |
| System.IndexOutOfRangeException | 在试图使用小于零或超出数组界限的下标索引数组时引发 |
| System.FormatException | 当格式输入不正确时所引发此异常 |
| System.NullReferenceException | 在需要使用引用对象的场合,如果使用了 null 引用,引发此异常 |
| System.OutOfMemoryException | 在分配内存(通过 new)的尝试失败时引发 |
| System.OverflowException | 在 checked 上下文中的算术运算溢出时引发 |
| System.ArgumentOutOfRangeException | 当参数值超出调用的方法所定义的允许取值范围时引发的异常。 |
| System.TypeInitializationException | 在静态构造函数引发异常并且没有可以捕捉到它的 catch 子句时引发 |
//实例1
static void Main(string[] args)
{
try
{
int[] a ={ 1, 3, 4, 5, 6 };
int sum = 0;
for (int i = 0; i < 6; i++)
sum += a[i];
Console.WriteLine("和为{0}", sum);
}
catch (ArgumentOutOfRangeException a)
{
Console.WriteLine(a.Message);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
Console.WriteLine(“无论怎样都要执行!");
}
}
//示例2
static int method(int a , int b )
{
if (a < 0)
throw new MyException(“被除数不能小于零”);
if (b = 0)
throw new DivideByZeroException(“除数不能等于零”);
int c = a / b;
return c;
}
自定义异常类
class MyException : System.ApplicationException
{
public MyException() { }
public MyException(string msg): base(msg) { }
public MyException(string msg,Exception e): base(msg,e){ }
}
数据库
ADO.NET核心对象
- Connection:用来建立与特定数据源得连接
- Command:用来对数据源执行SQL命令语句或存储过程
- DataReader:用来从数据源中获取只读,向前得数据流
- DataAdapter:用来在数据源和数据集之间交换数据
- DataSet:用来处理从数据源读出的数据,表示数据在内存中的缓存
联机模式
联机模式是指应用程序在处理数据的过程中,没有与数据库断开,一直与数据库保持连接状态
步骤
- 用Connection对象与数据库建立连接
- 用Command对象向数据库检索所需数据,或者直接进行编辑(插入,删除,修改)操作
- 如果Command对象向数据库执行的时数据检索操作,则把取回来的数据放在DataReader对象中读取;如果Command对象向数据库执行的是数据编辑操作,则直接进行步骤五(关闭Connection对象)
- 再完成数据的检索操作后,关闭DataReader对象
- 关闭Connection对象
脱机模式
脱机模式是指应用程序在处理数据之前与数据库连接来获取数据,在数据的处理过程中与数据库断开,处理完数据再与数据库连接来更新数据
在脱机模式下,应用程序并不一直保持到数据库的连接。首先打开数据连接并检索数据到DataSet中,然后关闭连接,用户可以操作DataSet中的数据,当需要更新数据或有其他请求时,就再次打开连接。典型的脱机存取模式
步骤
- 用Connection对象与数据库建立连接
- 用Command对象向数据库检索所需数据
- 用Command对象所取回来的数据,放在DataAdapter对象中
- 把DataAdapter对象的数据,填充到DataSet对象中
- 关闭Connection对象
- 所有数据存取,全部在DataSet对象中进行
- 再次打开Connection对象与数据库进行连接
- 利用DataAdapter对象对数据库进行更新
- 关闭Connection对象
脱机模式下的数据库存取,就是有需要时才和数据库联机。否则,都是和数据库保持脱机的状态
Connection
ConnectionString属性:Server=服务器名;Database=数据库名; UID=账户; PWD=密码
State属性:Open或Closed
Open方法
Close方法
SqlConnection 连接对象名 = new SqlConnection(连接字符串变量);
连接对象名.Open( );
连接对象名.Close( );
Command
- 创建Command对象,并设置其Command属性
- 创建CommandType和CommandText属性
- 调用相应方法来执行SQL命令
- 根据返回结果进行适当处理
SqlCommand comm = new SqlCommand ("select count(*) from studentInfo", conn); //conn是之前设置好的SqlConnection对象
int iCount = comm. ExecuteScalar ();
MessageBox.Show("studentInfo表中共有"+ iCount.ToString()+"条记录");
DataReader
SqlDataReader reader = comm. ExecuteReader();
//SqlConnection对象已设置好并打开,comm是之前设置好的SqlCommand对象
while (reader.Read())
{
// 读取一行数据
}
DataAdapter
常用属性
DataAdapter对象包含SelectCommand、InsertCommand、UpdateCommand和DeleteCommand四个属性
SelectCommand属性用于在数据库中选择记录,
InsertCommand属性用于在数据库中插入新记录,
UpdateCommand属性用于修改数据库中的记录,
DeleteCommand属性用于从数据库中删除记录。
常用方法
Fill方法: Fill方法用于执行SelectCommand,从数据库中获取数据来填充DataSet对象,并返回成功添加或刷新的行数。
Fill(DataSet dataSet)
Update方法:Update方法用于执行InsertCommand、UpdateCommand和DeleteCommand,把在DataSet对象进行的插入、修改或删除操作更新到数据库中,并返回成功更新的行数。
Update(DataSet dataSet)
创建并使用
OleDbDataAdapter adapter = new OleDbDataAdapter(comm);
//OleDbConnection对象已打开,comm是之前设置好的OleDbCommand对象
DataSet ds = new DataSet(); //创建数据集ds
adapter.Fill(ds); // 填充ds
// ……此处省略在ds中操作数据的代码
OleDbCommandBuilder builder = new OleDbCommandBuilder(adapter);
adapter.Update(ds); // 用ds更新数据库
DataSet
一个DataSet对象可以拥有多个DataTable和DataRelation对象,分别通过Tables和Relations属性对这两类对象进行管理。
DataTable对象相当于数据库中的表,一个DataTable对象可以拥有多个DataRow、DataColumn和Constraint对象;DataRow对象相当于数据表中的行,代表一条记录;DataColumn对象相当于数据表中的列,代表一个字段;Constraint对象相当于数据表中的约束,代表在DataColumn对象上强制的约束。
DataRelation对象相当于数据库中的关系,用来建立DataTable与DataTable间的父子关系。

浙公网安备 33010602011771号