面向对象程序设计(Object Oriented Programming,OOP)= 对象+类+继承+多态+消息。
程序由一系列对象组成。
类是现实世界抽象,包括数据和方法,对象是类的实例化。
对象间通过消息传递相互通信。
类是对象的定义,包含对象的信息,名称、方法、属性和事件。类并不是对象,因为它不存在于内存中。
类定义可在多个源文件间拆分,partial。
[类修饰符] class 类名{ 类的主体 }
类修饰符:
[访问修饰符] 数据类型 <成员变量名>
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建立类的新实例(对象)。
Car myCar; myCar = new Car();
(2)声明并创建对象。
Car myCar = new Car();
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 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修饰符要点:
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)用于对外部进行隐藏类的内部。
支持封装的修饰符:
属性提供将对象的读写和操作关联起来的机制,是一种特殊方法(访问器)。
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;
索引器用于简化类中数组或集合成员的存取操作。
[修饰符] 数据类型 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];
索引器与数组的区别:
索引器与属性的区别:
继承已有类的成员(数据和方法),方便创建和维护程序,重用代码有利于节省开发时间。
已有类为基类,继承类为派生类。
<访问修饰符> 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用于派生类中访问基类公有或保护成员的访问,只局限在构造函数,实例方法和实例属性访问器中。
class Car(){ public string type; public Car(){ car = "货车"; } } class ChineseCar:Car{ public string nation; public ChineseCar():base(){ nation = "中国"; } }
class Car(){ public void AddCar(){ } } class ChineseCar:Car{ public void AddTypee(){ base.AddCar(); } }
多层继承中:
同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。
虚方法重载,派生类中实现对基类虚方法的重新定义。
声明虚方法:
[访问修饰符] 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
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("交易结束,请带好你的贵重物品,欢迎下次光临!"); } }
实现方法的参数化,委托是一种引用方法的类型,类似函数指针的集合,为委托分配了多个方法(依序可重复),调用委托会一次调用分配的方法。
委托是类,定义了方法的类型,可以将方法当作参数传递,避免了大量if…else…(switch)语句,可扩展性。
委托与函数指针的区别:
[修饰符] 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