C#基础教程二
类与对象
概述
面向对象程序设计(Object Oriented Programming,OOP)= 对象+类+继承+多态+消息。
程序由一系列对象组成。
类是现实世界抽象,包括数据和方法,对象是类的实例化。
对象间通过消息传递相互通信。
基本概念
- 对象
对象是现实世界中事物存在的实体,分为静态部分(属性)和动态部分(可以变化的行为)。 - 类
类是具有相同属性和功能的对象的抽象集合,是C#的核心和基本构成模块。
类是一种抽象的数据类型,是对一类对象的统一描述。
类是对象的定义,包含对象的信息,名称、方法、属性和事件。类并不是对象,因为它不存在于内存中。
类、方法和变量
- 类的定义
类定义可在多个源文件间拆分,partial。
[类修饰符] class 类名{
类的主体
}
类修饰符:
- public:共有,访问不受限制。
- private:私有,私有成员只在声明的类或结构体中才可访问。
- protected:保护,类内部及继承类中才能访问。
- internal:内部,同一程序集内所有类可访问,一般限于本项目内。
- protected internal:内部保护,限于本项目或子类访问。
- 类的成员变量
[访问修饰符] 数据类型 <成员变量名>
static修饰用于静态成员。
非静态类中可以出现[非]静态成员,静态类中只能出现静态成员。
非静态方法中可以访问[非]静态成员,静态方法中只能访问静态成员。
调用方法不同,实例方法需要对象调用,即对象名.方法名,静态方法使用类调用,即类名.方法名。
静态类不允许创建对象。
工具类,可以考虑使用静态类(Console类),资源共享,但占用内存大。
- 类的成员方法的定义
[访问修饰符] 返回值类型 <方法名> ([参数列表]){
成员方法体
}
public class Car{
public double Number;
public string Make;
public string Model;
public string show(){
return string.Format("排量:{0}T,厂家:{1},型号:{2}",Numbetr,Make,Model);
}
}
对象的创建及使用
类是广义的数据类型,包含数据和数据的方法。
new建立类的新实例(对象)。
- 对象的创建和使用
(1)先声明对象,再创建对象初始化。
Car myCar;
myCar = new Car();
(2)声明并创建对象。
Car myCar = new Car();
- 访问类成员
(1)类内部访问。
this可省略。
this.类成员
this也可用于执行构造函数。
public class Test{
public Test(){
Console.WriteLine("无参构造函数");
}
//先执行Test(),后执行Test(string text)
public Test(string text):this(){
Console.WriteLine(text);
Console.WriteLine("有参构造函数");
}
}
(2)类外部调用。
对象名.成员
string str;
Car myCar = new Car();
myCar.Number = 2.0;
myCar.Make = "中国吉利";
myCar.Model = "GL";
str = myCar.show();
类的方法与属性
类的方法指对象执行的操作,类的属性指对象所拥有的特征。
方法定义
方法是将完成同一功能的内容放在一起,方便书写和调用。
访问修饰符 修饰符 返回值类型 方法名(参数列表){
语句块;
}
public double Add(double num1, double num2){
return num1+num2;
}
- 访问修饰符:public等,省略默认为private。
- 修饰符:virtual(虚拟的)、abstract(抽象的)、override(重写的,用于继承)、static(静态的)、sealed(密封的)。
- 返回值类型:任意数据类型,void表示无返回值。
- 方法名:方法功能的描述。
- 参数列表:0到多个参数。”数据类型 参数名“,多参数逗号隔开。
- 语句块:方法体,执行的代码块。
方法调用
使用方法的过程。
方法名(参数列表);
方法中的参数传递
方法调用从外部传递的参数是实参,方法内部接收的参数是形参。
形参调用时才分配内存,调用结束释放内存,只在方法内部有效。
- 值类型参数
无任何修饰符,按值传递,形参和实参互不影响。
public void Swap(int x, int y) {
}
- 引用型参数
传递变量引用,实参和形参的引用指向内存中的同一位置。形参的更改会影响实参指向的变量。方法定义和调用都必须使用ref。
public void Swap(ref int x, ref int y) {
int tmp;
tmp = x;
x = y;
y = tmp;
}
int a = 6, b = 8;
Swap(ref a, ref b);
- 输出型参数
不创建新的存储位置,形参使用实参的存储位置,方法定义和调用都使用out。
public void Add(out int a) {
a = 1;
}
int a;
Add(out a);
- 数组型参数
传递数组时,是引用传递,形参数组不能定义数组长度。
形参数组前不添加params修饰符,实参必须是数组名;形参数组前添加params修饰符,实参可以是数组名,也可以是数组元素值的列表(数据列表)。
params修饰符要点:
- 可以修饰任何类型参数;
- 只能修饰一维数组;
- 不允许params数组使用ref或out;
- 每个方法只能有一个params数组。
public int max1(int []a){
int k = 0;
for(int i=0;i<a.Length;i++){
if(a[i]>a[k])
k = i;
}
return a[k];
}
public int max2(params int []a){
int k = 0;
for(int i=0;i<a.Length;i++){
if(a[i]>a[k])
k = i;
}
return a[k];
}
int[] x = new int[]{1,2,3,4,5};
max1(x);
max2(x);
max2(1,2,3,4,5);
方法重载
定义多个方法名相同、参数列表(参数类型、参数个数)不同的方法。
int auto(int a, int b){
return a+b;
}
string auto(string a, string b){
return a+b;
}
int auto(int a, int b, int c){
return a+b+c;
}
类方法扩展实例
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace experiment2{
static class MyExtensions {//类名不固定
//是否对称字符串
public static bool IsPalindrome(this string str){
for(int i = 0, j = str.Length-1; i < j; i++, j--)
if(str[i] != str[j])
return false;
return true;
}
//数字反转
public static int ReverseDigits(this int num){//方法内num值的修改不会影响外部值
int Reverse_num = 0;
while(num!=0){
Reverse_num *= 10;
Reverse_num += num%10;
num /= 10;
}
return Reverse_num;
}
}
class Program{
static void Main(string[] args){
Console.Write("Enter a string: ");
string str = Console.ReadLine();
Console.WriteLine("\"" + str + "\"" + (str.IsPalindrome() ? " is " : " is not ") + "a palindrome");
Console.Write("Enter an integer: ");
int a = int.Parse(Console.ReadLine());
Console.WriteLine("The reverse of " + a.ReverseDigits() + " is " + a);
Console.ReadKey();
}
}
}
csc Program.cs
构造函数和析构函数
对象初始化和回收对象资源。
构造函数
构造方法,类的特殊成员方法,用来完成类的成员变量的自动初始化。
若编写了构造函数,系统不会提供默认构造函数。
- 无参构造函数
[访问修饰符] <类名>(){
构造函数的主体
}
class Employee{
public Employee(){}
}
- 有参构造函数
[访问修饰符] <类名>([参数列表]){
构造函数的主体
}
class Employee{
private string name;
public Employee(string n){
name = n;
}
}
析构函数
垃圾回收机制,当类实例不再有效,并符合析构条件时,会调用该类的析构函数。
~<类名>(){
析构函数主体
}
封装性
概述
面向对象编程语言中,编程单元是类,通过类封装数据和操作,类对外提供接口来访问和操作。
封装(Encapsulation)用于对外部进行隐藏类的内部。
支持封装的修饰符:
- private
- protected
- internal
- protected internal
- public
- other encapsulating strategy(其他封装策略):属性和索引器。
属性
属性提供将对象的读写和操作关联起来的机制,是一种特殊方法(访问器)。
class Person{
private string name;
private int age;
public void SetName(string n){
this.name = n;
}
public void SetAge(int a){
this.age = a;
}
public string GetName(){
return this.name;
}
public int GetAge(){
return this.age;
}
}
string name;
int age;
Person p = new Person();
p.SetName("ma");
p.SetAge(30);
name = p.GetName();
age = p.GetAge();
class Person{
private string name;
private int age;
public string Name(){
set{this.name = value;}
get{return this.name;}
}
public int Age(){
set{this.age = value;}
get{return this.age;}
}
}
string name;
int age;
Person p = new Person();
p.Name = "ma";
p.Age = 30;
name = p.Name;
age = p.Age;
- 属性是像类的字段一样的访问方法。
- 属性只能包含一个get访问器和一个set访问器,不能含其他方法、字段等。
- get访问器返回属性类型值。
索引器
索引器用于简化类中数组或集合成员的存取操作。
[修饰符] 数据类型 this[索引类型 index] {
get{//获得属性的代码}
set{//设置属性的代码}
}
public class ID{
private string[] name = new string[2];
public string this[int index] {
get{return name[index];}
set{name[index] = value;}
}
}
string name;
ID id = new ID();
id[0] = "ma";
id[1] = "hong";
name = id[0];
索引器与数组的区别:
- 数组索引值为整数,索引器索引值(Index)不限定为整数。
- 索引器允许重载。
- 索引器不是变量,未定义直接存储数据的内存,数组有。索引器有get和set访问器。
索引器与属性的区别:
- 索引器以方法签名(this)标识,属性采用名称标识。
- 索引器可以重载,属性不可以。
- 索引器不能用static声明(索引器属于实例成员),属性可以。
类的继承
概述
继承已有类的成员(数据和方法),方便创建和维护程序,重用代码有利于节省开发时间。
已有类为基类,继承类为派生类。
<访问修饰符> class<基类名称> {
基类主体部分
}
class <派生类>:<基类名称> {
派生类主体部分
}
public class Car{
private string type;
public string Type(){
set{this.type = value;}
get{return this.type;}
}
}
public class ChineseCar:Car{
private string nation;
public string Nation(){
set{this.nation = value;}
get{return this.nation;}
}
}
string type;
string nation;
ChineseCar car = new ChineseCar();
car.Type = "type1";
car.Nation = "China";
type = car.Type;
nation = cat.Nation;
- 构造函数和析构函数不能被继承
- 派生类中同名成员会覆盖基类同名成员(不会删除)。
- 基类定义虚方法、虚属性及虚索引器,派生类重载,从而实现多态。
- 派生类只能单一继承,接口可实现多重继承。
base使用
base用于派生类中访问基类公有或保护成员的访问,只局限在构造函数,实例方法和实例属性访问器中。
- base调用基类构造方法;
class Car(){
public string type;
public Car(){
car = "货车";
}
}
class ChineseCar:Car{
public string nation;
public ChineseCar():base(){
nation = "中国";
}
}
- base在派生类中调用基类方法。
class Car(){
public void AddCar(){
}
}
class ChineseCar:Car{
public void AddTypee(){
base.AddCar();
}
}
多层继承中:
- 重载存在,base指向直接继承的父类成员的方法;
- 无重载存在,base指向任何上级父类的公有或保护方法。
多态
概述
同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。
- 编译时多态:重载实现,非虚成员,系统在编译时,根据传递的参数、返回类型等信息决定操作。
- 运行时多态:重写虚函数实现,系统运行时,根据实际情况决定操作。
实现多态的方式
- 多个类继承同一个类,继承实现多态,派生类重写基类成员。
- 抽象类被派生类扩充使用,抽象类实现多态,抽象类中部分或全部未实现的成员在派生类中全部实现;抽象类中已实现成员仍可被重写。
- 多个类实现相同接口,接口实现多态。
虚方法与重写
虚方法重载,派生类中实现对基类虚方法的重新定义。
声明虚方法:
[访问修饰符] virtual [返回类型] ([参数列表]) {
虚方法实现部分
}
派生类中覆盖同名方法使用override。
public class oblong{
protected double x, y;
public const double p = Math.PI;
public oblong(double x1, double y1){
x = x1;
y = y1;
}
public virtual double Area(){
return x*y;
}
}
public class round:oblong{
public round(double r):base(r, 0){}
public override double Area(){
return p*x*x;
}
}
round r = new round(2);
r.Area();
oblong ob = new oblong(2,3);
ob.Area();
派生类中创建同名方法使用new。
public class oblong{
protected double x, y;
public const double p = Math.PI;
public oblong(double x1, double y1){
x = x1;
y = y1;
}
public virtual double Area(){
return x*y;
}
}
public class round:oblong{
public round(double r):base(r, 0){}
public new double Area(){
return p*x*x;
}
}
round r = new round(2);
r.Area();
oblong ob = new oblong(2,3);
ob.Area();
override和new的相同点是定义与基类相同的方法,派生类对象执行各自的派生类中的方法;
不同点是派生类对象向上转型后,重写(override)基类调用派生类方法,隐藏(new)基类调用基类方法。
抽象类与抽象方法
抽象类是不能被实例化的类(至少含有一个抽象方法,抽象方法必须实现),只能用来派生类。
抽象方法只能声明,派生类中实现。
[访问修饰符] abstract class 类名{
//抽象类成员定义
}
[访问修饰符] abstract void 方法名(方法参数);
public abstract class oblong{
protected double x, y;
public const double p = Math.PI;
public oblong(double x1, double y1){
x = x1;
y = y1;
}
public abstract double Area();
}
public class round:oblong{
public round(double r):base(r, 0){}
public override double Area(){ //override或new都行
return p*x*x;
}
}
public class retangle:oblong{
public retangle(double l, double w):base(l, w){}
public override double Area(){ //override或new都行
return x*y;
}
}
round r = new round(2);
r.Area();
rectangle ra = new rectangle(2,3);
ra.Area();
//错误
//oblong b = new oblong(2,3);
猜拳游戏
Program.cs
using System;
namespace Game{
class Program{
static void Main(string[] args){
Player p1 = new Player(){ Name = "Tony" };
Computer c1 = new Computer();
Judge j1 = new Judge();
while(true){
int res1 = p1.ShowFist();
int res2 = c1.ShowFist();
j1.Determine(res1, res2);
Console.ReadKey();
}
}
}
}
Player.cs
using System;
namespace Game{
class Player{
string name;
public string Name{
get { return name; }
set { name = value; }
}
public int ShowFist(){
Console.WriteLine("请问,你要出什么拳? 1.剪刀 2.石头 3.布");
int result = ReadInt(1, 3);
string fist = IntToFist(result);
Console.WriteLine("玩家{0}出了{1}", name, fist);
return result;
}
private string IntToFist(int input){
string result = string.Empty;
switch(input){
case 1:result = "剪刀";
break;
case 2:result = "石头";
break;
case 3:result = "布";
break;
}
return result;
}
private int ReadInt(int min, int max){
while(true){
//从控制台获取用户输入的数据
string str = Console.ReadLine();
//将用户输入的字符串转换成Int类型
int result;
if(int.TryParse(str, out result)){
//判断输入的范围
if(result >= min && result <= max){
return result;
}else{
Console.WriteLine("请输入1个{0}-{1}范围的数", min, max);
continue;
}
}else{
Console.WriteLine("请输入整数");
}
}
}
}
}
Computer.cs
using System;
namespace Game{
class Computer{
//生成一个随机数,让计算机随机出拳
Random ran = new Random();
public int ShowFist(){
int result = ran.Next(1, 4);
Console.WriteLine("计算机出了{0}", IntToFist(result));
return result;
}
private string IntToFist(int input){
string result = string.Empty;
switch(input){
case 1:result = "剪刀";
break;
case 2:result = "石头";
break;
case 3:result = "布";
break;
}
return result;
}
}
}
Judge.cs
using System;
namespace Game{
class Judge{
public void Determine(int p1, int p2){
if(p1 - p2 == -2 || p1 - p2 == 1){
Console.WriteLine("玩家胜利!");
}else if(p1 == p2){
Console.WriteLine("平局!");
}else{
Console.WriteLine("玩家失败!");
}
}
}
}
csc Program.cs Player.cs Computer.cs Judge.cs
系统登录用户类
Program.cs
using System;
namespace myGame{
class Program{
static void Main(string[] args){
gameUser guer1 = new gameUser("剑士", "123456");
Console.WriteLine("玩家:" + guer1.Username + ", 密码:" + guer1.Password);
Console.Read();
}
}
}
gameUser.cs
namespace myGame{
class gameUser{
private string username;
public string Username{
get { return username; }
set { username = value; }
}
private string password;
public string Password{
get { return password; }
set { password = value; }
}
public gameUser(){}
public gameUser(string username,string password){
this.username = username;
this.password = password;
}
}
}
csc Program.cs gameUser.cs
继承与多态实例
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
public class POS{
private string area;
public POS(string area){
this.area = area;
}
//public string Area{ get; set; } //始终为空,即使area有值
public string Area{ get {return area;} set {area=value;} }
}
public interface IPayable{
void Pay(decimal money, POS pos);
}
class BankCard{
private string _account;
decimal _savings;
public BankCard(string account){
_account = account;
}
public virtual void Query(){ // 查询
Console.WriteLine("银行卡{0}上余额为{1}", _account, _savings);
}
public virtual void Deposit(decimal money){ // 存款
_savings += money;
_savings = Math.Round(_savings, 2);
}
public virtual void Draw(decimal money){ // 取款
if (_savings >= money){
_savings -= money;
_savings = Math.Round(_savings, 2);
}else
Console.WriteLine("余额不足");
}
class PayableCard : BankCard, IPayable{
public PayableCard(string account):base(account){
//_account = account;
}
public virtual void Pay(decimal money, POS pos){
if(_savings >= money){
_savings -= money;
_savings = Math.Round(_savings, 2);
Console.WriteLine("支付{0}元", money);
}else
Console.WriteLine("余额不足,无法进行支付");
}
}
class LocalCard : PayableCard{
private string area;
public LocalCard(string account, string area):base(account){
//_account = account;
this.area = area;
}
public override void Pay(decimal money, POS pos){
if(area == pos.Area){
if(_savings >= money){
_savings -= money;
_savings = Math.Round(_savings, 2);
Console.WriteLine("支付{0}元", money);
}
else
Console.WriteLine("余额不足,无法进行支付");
}else
Console.WriteLine("本地卡不支持异地支付.");//银行卡和POS机不是同一个地方
}
}
class GlobalCard : PayableCard{
private string area;
decimal _rate = 0.01m;
public GlobalCard(string account, string area):base(account){
//_account = account;
this.area = area;
}
public override void Pay(decimal money, POS pos){
if(area != pos.Area){
_savings -= money * (1 + _rate);
_savings = Math.Round(_savings, 2);
Console.WriteLine("支付{0}元,手续费{1}元", money * (1 + _rate), money * _rate);
}else{
_savings -= money;
_savings = Math.Round(_savings, 2);
Console.WriteLine("支付{0}元", money);
}
}
}
class CreditCard : PayableCard{
decimal _limit;
decimal overdraw;
public CreditCard(string account, decimal limit):base(account){
//_account = account;
_limit = limit;
}
public override void Pay(decimal money, POS pos){
if(_savings < 0)
overdraw = -_savings;
else
overdraw = 0;
if(_savings + _limit < money)
Console.WriteLine("支付超出透支额度.");
else{
_savings -= money;
_savings = Math.Round(_savings, 2);
Console.WriteLine("支付{0}元", money);
}
Console.WriteLine("透支额度为{0}", _limit);
Console.WriteLine("欠款为{0},余额为{1}", overdraw, _savings);
}
}
class Program{
static void Main(string[] args){
POS pos1 = new POS("010");
POS pos2 = new POS("021");
BankCard[] cards = new BankCard[4];
cards[0] = new BankCard("bj10000001");
cards[1] = new LocalCard("bj90000009", "010");
cards[2] = new GlobalCard("sh30000001", "021");
cards[3] = new CreditCard("sh80000008", 10000);
for(int i = 0; i < cards.Length; i++){
cards[i].Deposit(2200);
Console.WriteLine("{0}支付前", cards[i]);
cards[i].Query();
if(cards[i] is IPayable){
((IPayable)cards[i]).Pay(1000, pos1);
((IPayable)cards[i]).Pay(1190, pos2);
}
Console.WriteLine("{0}支付后", cards[i]);
cards[i].Query();
Console.WriteLine();
}
}
}
}
csc Program.cs gameUser.cs
接口
C#类不支持多重继承,通过接口实现多重继承。
接口概念
接口是契约、标准,只能定义方法、属性、索引器和事件等成员(不能实现),与抽象类类似(但接口所有成员完全抽象)。
类可以继承多个接口,接口可以继承接口,类可以通过继承基类(或接口)多次继承同一个接口。
接口特点:
- 类似抽象基类,继承接口的非抽象类必须实现接口。
- 接口不能直接实例化。
- 接口可包含事件、索引器、方法和属性。
- 接口不包含方法的实现。
- 类和结构可从多个接口继承。
- 接口自身可从多个接口继承。
接口声明
修饰符 interface 接口名称:继承的接口列表{
接口内容;
}
接口中成员默认public,不允许添加访问修饰符。
interface Information{
string Code{get;set;}
string Name{get;set;}
void ShoowInfo();
}
接口应用
账户接口1,所有银行账户类都要继承此接口,基本功能。
public interface IBankAccount{
void PayIn(decimal amount); //存钱
bool Withdraw(decimal amount);//取钱
decimal Balance{get;}//余额
}
普通账户类1
public class SaverAccount:IBankAccount{
private decimal balance;
public void PayIn(decimal amount){
balance += amount;
}
public bool Withdraw(decimal amount){
if(balance>=amount){
balance -= amount;
return true;
}else{
Console.WriteLine("余额不足!");
return false;
}
}
public decimal Balance{get{return balance;}}
public override string string ToString(){
return String.Format("Saver Bank balance:", balance);
}
}
账户接口2,包含高级银行账户的一些额外功能。
public interface IBankAdvancedAccount{
void DealStartTip(); //交易开始提示
void DealStopTip();//交易结束提示
}
金卡账户类2
public class GoldAccount:IBankAccount,IBankAdvancedAccount{
private decimal balance;
public void PayIn(decimal amount){
balance += amount;
}
public bool Withdraw(decimal amount){
if(balance>=amount){
balance -= amount;
return true;
}else{
Console.WriteLine("余额不足!");
return false;
}
}
public decimal Balance{get{return balance;}}
public override string string ToString(){
return String.Format("Saver Bank balance:", balance);
}
public void DealStartTip(){
Console.WriteLine("交易开始,请注意周围环境!");
}
public void DealStopTip(){
Console.WriteLine("交易结束,请带好你的贵重物品,欢迎下次光临!");
}
}
接口实例
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//Array.Sort()方法需要数组中元素实现IComparable接口
public class Person : IComparable<Person>{
public string FirstName;
public string LastName;
public DateTime Birthday;
public Person() { }
public Person(string FirstName, string LastName, DateTime Birthday){
this.FirstName = FirstName;
this.LastName = LastName;
this.Birthday = Birthday;
}
public int CompareTo(Person p){
if(this.LastName.CompareTo(p.LastName) == 0)
return this.FirstName.CompareTo(p.FirstName);
else
return this.LastName.CompareTo(p.LastName);
}
public override string ToString(){
return "FirstName=" + FirstName + ", " + "LastName=" + LastName + ", " + "Birthday=" + Birthday;
}
}
//实现IComparer接口的比较类
class PersonComparer : Person, IComparer<Person>{
public PersonComparer(){
this.Birthday = Birthday;
}
public int Compare(Person p1, Person p2){
return p1.Birthday.CompareTo(p2.Birthday);
}
}
class Program{
static void Main(string[] args){
Person[] persons = new Person[]{
new Person { FirstName = "Damon", LastName = "Hill", Birthday = new DateTime(1990, 5, 1) },
new Person { FirstName = "Niki", LastName = "Lauda" , Birthday = new DateTime(1995, 10, 4) },
new Person { FirstName = "Ayrton", LastName = "Senna" , Birthday = new DateTime(1992, 6, 23) },
new Person { FirstName = "Graham", LastName = "Hill" , Birthday = new DateTime(1994, 9, 15) }
};
Console.WriteLine("Order by name:");
Array.Sort(persons);
foreach(var p in persons)
Console.WriteLine(p);
Console.WriteLine("Order by Birthday:");
Array.Sort(persons, new PersonComparer());
foreach var p in persons)
Console.WriteLine(p);
}
}
csc Program.cs
委托
实现方法的参数化,委托是一种引用方法的类型,类似函数指针的集合,为委托分配了多个方法(依序可重复),调用委托会一次调用分配的方法。
委托定义
委托是类,定义了方法的类型,可以将方法当作参数传递,避免了大量if…else…(switch)语句,可扩展性。
委托与函数指针的区别:
- 安全性:c/c++函数指针只是传递函数地址,无类型安全性;.NET中委托类型安全。
- 与实例关联性:方法通常与类实例关联,委托可以获取类实例信息,从而实现与类实例的关联。
- 函数指针是指针变量,分配在栈中;委托声明是类,类实例化为对象,分配在堆中。
- 委托可以指向不同类中相同类型的方法,函数指针不可以。
委托声明
[修饰符] delegate [返回值类型] [委托名称] ([参数列表])
[修饰符]可选。
委托是一种数据类型,派生于System.Delegate的派生类。
委托应用
Test.cs
using System;
class Test{
delegate int MyDelegate(int x, int y);//x,y不能省略
int Add(int x, int y){
return x+y;
}
static void Main(){
Test tc = new Test();
MyDelegate md = tc.Add;
Console.WriteLine(md(2,3));//等价于md.Invoke(2,3)
}
}
csc Test.cs
Heater.cs
using System;
using System.Threading;
class Heater{
int temperature;
string type = "RealFire 001";
string area = "China CQ";
public class BoiledEventArgs:EventArgs{
public readonly int temperature;
public BoiledEventArgs(int temperature){
this.temperature = temperature;
}
}
//声明委托
delegate void BoiledEventHandler(Object sender, BoiledEventArgs e);
//声明事件
event BoiledEventHandler Boiled;
protected virtual void OnBoiled(BoiledEventArgs e){
if(Boiled!=null)
Boiled(this, e);
}
void BoilWater(){
for(int i=0;i<=100;i++){
temperature = i;
if(temperature>95){
BoiledEventArgs e = new BoiledEventArgs(temperature);
OnBoiled(e);
}
}
}
class Alarm{
public void MakeAlert(Object sender, BoiledEventArgs e){
Heater heater = (Heater)sender;
Console.WriteLine("Alarm:{0}-{1}", heater.area, heater.type);//内部类可以访问外部内私有变量
Console.WriteLine("Alarm:嘀嘀嘀,水已经{0}℃了", e.temperature);
Console.WriteLine();
}
}
class Display{
public static void ShowMsg(Object sender, BoiledEventArgs e){
Heater heater = (Heater)sender;
Console.WriteLine("Display:{0}-{1}", heater.area, heater.type);//内部类可以访问外部内私有变量
Console.WriteLine("Display:水快烧开了,当前温度:{0}℃", e.temperature);
Console.WriteLine();
Thread.Sleep(1000);
}
}
class Program{
static void Main(){
Heater heater = new Heater();
Alarm alarm = new Alarm();//内部类可以创建私有内部类,访问私有内部类的方法需要public
heater.Boiled = alarm.MakeAlert;//给事件赋值方法
//heater.Boiled += alarm.MakeAlert;//Boiled为null,与上面等价
heater.Boiled += (new Alarm()).MakeAlert;//匿名对象注册方法
heater.Boiled += new BoiledEventHandler(alarm.MakeAlert);//通过委托对象注册方法
heater.Boiled += Display.ShowMsg;//注册静态方法
heater.BoilWater();//烧水,自动调用注册过的方法
}
}
}
csc Heater.cs
匿名方法
匿名方法允许一个与委托关联的代码被内联地写入使用委托的位置,可以共享对本地语句包含的方法成员的访问。
delegate(【参数列表】){
【代码块】
}
Program.cs
using System;
using System.Threading;
class Program{
delegate void DelOutput(string s);
static void NamedMethod(string k){
Console.WriteLine(k);
}
static void Main(){
DelOutput del = delegate(string j){
Console.WriteLine(j);
};
del.Invoke("匿名方法被调用");
Console.WriteLine();
del = NamedMethod;
del.Invoke("静态方法被调用");
Console.WriteLine();
}
}
csc Program.cs
委托实例
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
class Delegates{
//创建委托类型
public delegate bool NumberPredicate(int number);
static void Main(string[] args){
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//生成委托实例
NumberPredicate evenPredicate = IsEven;
//利用委托变量调用IsEven
Console.WriteLine("Call IsEven using a delegate variable: {0}", evenPredicate(2));
//选出偶数
List<int> evenNumbers = FilterArray(numbers, evenPredicate);
//描述并输出
DisplayList("Use IsEven to filter even numbers: ", evenNumbers); //滤过
//选出素数并输出
NumberPredicate primePredicate = IsPrime;
List<int> Prime = FilterArray(numbers, primePredicate);
DisplayList("Use IsPrime to filter even numbers: ", Prime);
}
private static List<int> FilterArray(int[] intArray, NumberPredicate predicate){
List<int> numbers = new List<int>();
for(int i = 0; i < intArray.Length; i++)
if(predicate(intArray[i]))
numbers.Add(intArray[i]);
return numbers;
}
//偶数判断函数
private static bool IsEven(int number){
return (number % 2 == 0);
}
//判断是否素数
private static bool IsPrime(int number){
if(number <= 1)
return false;
else
for(int i = 2; i <= Math.Sqrt(number); i++)
if(number % i == 0)
return false;
return true;
}
//列表元素输出
private static void DisplayList(string description, List<int> list){
Console.Write(description);
foreach(int number in list)
Console.Write(number + " ");
Console.WriteLine();
}
}
csc Program.cs
事件
引发事件,发生与对象相关事件(对象数据修改,进程完成,服务中断等)时,类将事件通知给用户,引发事件的对象称为事件源或发送者。
事件定义
事件与委托相关,发生事件,产生动作。
事件使用
使用事件步骤:
①创建委托;
②关联委托与事件;
③编写事件处理程序;
④事件处理程序生成委托实例;
⑤订阅事件(委托实例添加到产生事件对象的事件列表中)。
- 定义事件
先定义委托,后定义事件。
<访问修饰符> event 委托名 事件名;
- 事件的发布和订阅
通过委托实现事件的发布(执行)和订阅(注册),执行以下步骤:
①定义委托和关联事件;
②发布器中写触发事件的条件(方法、其他事件等);
③订阅器中编写处理事件的方法程序等;
④通过事件订阅处理事件的方法程序。
<事件> += new <事件关联委托>(<处理事件的方法名>);
Program.cs
/*
C#处理事件采用发布-订阅模式(publisher-subscriber model)
包含委托和事件申明的类是发布器
包含事件处理的类是订阅器
*/
using System;
public delegate void mydelegate();//定义与事件关联的委托类
class Mum{//定义事件发布者母亲类
public event mydelegate EatEvent;//定义事件
public void Cook(){
Console.WriteLine("母亲:饭好了,来吃饭。");
EatEvent();//触发事件
}
}
class Son{//定义订阅器儿子类
public void Eat(){
Console.WriteLine("儿子:妈,我写完作业再来。");
}
}
class Father{//定义订阅器父亲类
public void Eat(){//事件处理方法
Console.WriteLine("母亲:老婆,我来吃饭了。");
}
}
class Program{
static void Main(){//事件处理方法
Mum mum = new Mum();
Father father = new Father();
Son son = new Son();
mum.EatEvent += new mydelegate(son.Eat);//不能使用=
mum.EatEvent += new mydelegate(father.Eat);
mum.Cook();
}
}
csc Program.cs
事件实例
csc Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
public static class Sun{
public static event EventHandler OnRise; //EventHandler 为委托名
public static void Rise(){
Console.WriteLine("太阳从东方升起!");
if(OnRise != null)
OnRise(null, null);
}
}
public class Cock{
private string _name;
public Cock(string name){
_name = name;
Sun.OnRise += new EventHandler(Sun_OnRise);
}
private void Sun_OnRise(object sender, EventArgs e){//太阳升起引发公鸡打鸣
Console.WriteLine("公鸡{0}:", _name);
Console.WriteLine("雄鸡一声天下白!");
this.Sing();
}
public event EventHandler OnSing;
public void Sing(){
Console.WriteLine("喔喔喔……");
if(OnSing != null)
OnSing(this, null);
}
}
public class Host{
private string _name;
public Host(string name) { _name = name; }
public event EventHandler OnGetup;
public void Getup(){
Console.WriteLine("日出而作……");
if (OnGetup != null)
OnGetup(this, null);
}
public void Foster(Cock c1){
c1.OnSing += new EventHandler(c1_OnSing);
}
private void c1_OnSing(object sender, EventArgs e){//公鸡打鸣引起主人起床
Console.WriteLine("主人{0}:", _name);
Console.WriteLine("闻鸡起舞!");
this.Getup();
}
}
public class Dog{
private string _name;
private Host _owner;
public Dog(string name){
_name = name;
}
public Host Owner{
get { return this._owner; }
set{
this._owner = value;
Owner.OnGetup += new EventHandler(owner_OnGetup);
}
}
private void owner_OnGetup(object sender, EventArgs e){//主人起床引起狗叫
Console.Write("狗 {0}:", _name);
Console.WriteLine("汪汪");
}
}
public class Cat{
private string _name;
private Host _owner;
public Cat(string name){
_name = name;
}
public Host Owner{
get { return this._owner; }
set{
this._owner = value;
Owner.OnGetup += new EventHandler(owner_OnGetup);
}
}
private void owner_OnGetup(object sender, EventArgs e){//主人起床引起猫叫
Console.Write("猫 {0}:", _name);
Console.WriteLine("喵喵");
}
}
class Program{
static void Main(string[] args){
Cock cock1 = new Cock("花花");
Host host1 = new Host("祖逖");
Dog dog1 = new Dog("旺财");
Cat cat1 = new Cat("咪咪");
host1.Foster(cock1);
dog1.Owner = host1;
cat1.Owner = host1;
Sun.Rise();
}
}
csc Program.cs