一.面向对象OOP
三大特点:数据的封装、继承、多态
二.继承
1.语法
class ChildClass : ParentClass
2.举例
public class Pet { public string Name; public void PrintName() { Console.WriteLine("Pet's name is" + Name); } } public class Dog:Pet { } public class Cat:Pet { } class Program { static void Main(string[] args) { Dog dog = new Dog(); dog.Name = "Jack"; dog.PrintName(); Cat cat = new Cat(); cat.Name = "Tom"; cat.PrintName(); } }
3.特殊的基类
Object类是所有类的共同基类,是唯一的非派生类。
4.规则
一个类只能继承一个父类。
三.隐藏方法
1.语法
屏蔽数据成员:在派生类中声明名称和类型相同的成员
屏蔽函数成员:在派生类中声明新的带有相同函数签名的成员
让编译器知道:可以添加new关键字,否则会有警告
2.举例
public class Pet { public string Name; public void PrintName() { Console.WriteLine("Pet's name is " + Name); } } public class Dog:Pet { new public void PrintName() { Console.WriteLine("宠物的名字是" + Name); } } public class Cat:Pet { } class Program { static void Main(string[] args) { Dog dog = new Dog(); dog.Name = "Jack"; dog.PrintName(); Cat cat = new Cat(); cat.Name = "Tom"; cat.PrintName(); } }
四.虚方法和多态
1.设计原则:依赖倒置原则
程序设计要依赖于抽象类(Pet),而不依赖于具体类(Dog)
2.基类的引用
当用基类类型的引用指向派生类时,仅仅能访问派生类中的基类部分
例如上面的例子,如果把dog的引用改为从基类Pet引用,那么之前在Dog中的隐藏方法就不能访问
......
class Program { static void Main(string[] args) { Pet dog = new Dog(); dog.Name = "Jack"; dog.PrintName(); Cat cat = new Cat(); cat.Name = "Tom"; cat.PrintName(); } }
运行结果中,dog的名字就是英文形式,而不是中文形式
3.统一提高效率
需要一个容器(比如数组)保存所有的基类(Pet)
4.子类具有差异
5.虚方法和多态的武器
可以解决既有统一的方法属性,又可以体现派生类的个性
声明为virtual的方法就是虚方法
基类的虚方法可以在派生类中用override进行重写
因为为了统一管理,一般都是用采用基类引用,所以再加上虚方法,就会达到兼顾统一又张扬个性的目的,而此时的情况更符合下图的描述
即,当pet调用speak方法时,由于是虚方法,所以程序会去找实际是dog派生类,所以就会调用dog的重写方法
但是,如果是dog直接调用speak方法,程序就直接调用speak方法
6.例如
public class Pet { public string Name; public void PrintName() { Console.WriteLine("Pet's name is " + Name); } virtual public void Speak() { Console.WriteLine(Name+" is speaking"); } } public class Dog:Pet { new public void PrintName() { Console.WriteLine("宠物的名字是" + Name); } override public void Speak() { Console.WriteLine(Name + " is speaking "+"WoWo"); } } public class Cat:Pet { override public void Speak() { Console.WriteLine(Name + " is speaking " + "MiMi"); } } class Program { static void Main(string[] args) { Pet dog = new Dog(); dog.Name = "Jack"; dog.PrintName(); dog.Speak(); Cat cat = new Cat(); cat.Name = "Tom"; cat.PrintName(); cat.Speak(); } }
7.管理
例如
public class Pet { public string Name; public void PrintName() { Console.WriteLine("Pet's name is " + Name); } virtual public void Speak() { Console.WriteLine(Name+" is speaking"); } } public class Dog:Pet { public Dog(string name) { Name = name; } new public void PrintName() { Console.WriteLine("宠物的名字是" + Name); } override public void Speak() { Console.WriteLine(Name + " is speaking "+"WoWo"); } } public class Cat:Pet { public Cat(string name) { Name = name; } override public void Speak() { Console.WriteLine(Name + " is speaking " + "MiMi"); } } class Program { static void Main(string[] args) { Pet[] pets = new Pet[] { new Dog("Jack"), new Cat("Tom") }; for(int i=0;i<pets.Length;++i) { pets[i].Speak(); } } }
8.关于虚方法的其它知识点
重写虚方法时,基类方法不能是private
static方法不能重写
方法、属性、索引器、事件,都可以用虚方法或重写
五.派生类和构造函数
1.构造函数就是初始化一个构造对象
在执行派生类的构造函数体之前,将会隐式或显示调用基类构造函数
如下图的调用顺序所示
2.调用基类构造函数
3.调用当前类的其它构造函数
六.抽象类和抽象方法
1.抽象方法语法
abstract public void Func();
在基类不可以有函数体,只能在派生类中用override重写
2.当一个class含有一个抽象方法,就是抽象类
abstract class Pet
{
......
}
抽象类可以全是抽象成员,也可以全是普通成员(可以不声明abstract),也可以是它们的组合
抽象类的抽象成员在派生类中必须用override来重写
七.密闭类和密闭方法
1.用sealed关键字即为密闭类和密闭方法
不希望其他人继承修改某些类,或者不希望其他人重写某些方法,不希望派生出子类
2.如果一个基类方法不希望子类对其重写,不声明为virtual就行
如果是派生类方法不希望子类对其重写,同时是override重写,就可以用sealed机制
八.接口
1.接口是一种引用类型,指定一组函数成员但是不实现这些函数
2.语法
interface ICatchMice // 接口名称一般以I开头
{
void CatchMice(); // 默认public,但不能加public
}
3.接口是用来被继承类实现
Cat : ICatchMice
{
public void CatchMice() {......}
}
4.接口也是一种引用类型
Cat c = new Cat();
ICatchMice ic = (ICatchMice)c;
c.CatchMice (); //通过对象调用
ic.CatchMice (); //通过接口调用
5.接口可以实现一个类有多个接口
Cat : Pet, ICatchMice, IClimbTree
{
public void CatchRat(){...}
public void ClimbTree(){...}
...
}
6.例如
namespace PetShop { interface ICatchMice { void CatchMice(); } interface IClimbTree { void ClimbTree(); } abstract public class Pet { public Pet(string name) { _name = name; } protected string _name; public void PringName() { Console.WriteLine("Pet's name is " + _name); } abstract public void Speak(); virtual public void Move() { } } public class Dog:Pet { public Dog(string name):base(name) { } new public void PrintName() { Console.WriteLine("宠物的名字是" + _name); } sealed override public void Speak() { Console.WriteLine(_name + " is speaking:" + "WoWo"); } public override void Move() { } } public class Labrador:Dog { public Labrador(string name):base(name) { } } public class Cat:Pet,ICatchMice,IClimbTree { public Cat(string name):base(name) { } public override void Speak() { Console.WriteLine(_name + " is speaking:" + "MiMi"); } public void CatchMice() { Console.WriteLine("Catch mice"); } public void ClimbTree() { Console.WriteLine("Climb tree"); } } class Program { static void Main(string[] args) { Pet[] pets = new Pet[] { new Dog("Jack"), new Cat("Tom") }; for (int i=0; i<pets.Length;++i) { pets[i].Speak(); } Cat c = new Cat("Tom2"); IClimbTree climb = (IClimbTree)c; c.ClimbTree(); climb.ClimbTree(); ICatchMice catchM = (ICatchMice)c; c.CatchMice(); catchM.CatchMice(); } } }
运行结果
7.小贴士
接口并不是对一个类的补充描述,它是泛化的类,不是具体的事物,高级用法还可以是行为的组合。
比如,公司要招人,所签的合同就是接口。
屋主要打扫卫生,用猫或狗甚至人来抓屋子里的老鼠,这个抓老鼠就是接口。
学生要请家教来上课,上课就是接口,这个老师不满意可以换下一个老师。
人饿了,要用网站来订餐,这其中的饿了吃饭就是接口,必胜客不满意可以换麦当劳。
九.结构和类
1.不同点
结构是值类型(在栈中),类是引用类型(在堆中)
结构不支持继承,类支持继承
结构不能定义默认构造函数,编译器会定义
2.适用场合
结构:由于分配内存快,作用域结束即被删除,不需要垃圾回收,由于小型数据结构。
但传递过程中会复制,应该使用ref提高效率。
类:由于其他的需要继承体系的场合
3.例如
在PetShop中定义
struct fish
{
int weight;
int size;
int type;
}
之后在Cat中调用
4.小贴士
普通编程中很少用到结构,一般高手才会用结构。