一.面向对象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.小贴士
普通编程中很少用到结构,一般高手才会用结构。