Day 1
Day2 继承
What:是复用代码,复用概念的一种技术。
【代码复用的一种方式】
Why:为了复用代码,复用概念,方便管理多个类【层次,树】
Who、where,when:只要用C#写代码,就在使用【无意识】
【有意识:刻意用 :父类】
没有明确指定父类,默认父类是object
Object是任何类的直接或者间接父类!
一切皆对象【都是object对象类的子类】
为什么需要继承
为了保留原有的功能,
通过继承可以复用,不用每次都从头开始
为了能够对类进行层次性的管理 狗 猫 动物
继承的优点
1. 复用代码的一种方式
2. 统一概念,概念复用 复用概念 动物》狗 语法上
以层次化【树】的方式管理类 动物
——————
| |
狗 猫
继承的缺点
耦合度高:父类的改变直接影响到所有的子类,
而不需要通知子类
建议:
继承层次不要太深,三层即可
尽量选择抽象类来继承
继承的语法
写法:class A: B 表示A类继承B类,
A类称为子类(派生类),
B类称为父类(基类,超类)
继承的特点:
1父类中所定义除私有成员外都继承给子类
【子类拥有父类中所有非私有成员】
2构造方法不继承给子类【公共,私有】
结论:因为继承,调用父类中的方法,有三种调用法
// method 1:
Animal obj= new Animal();
obj.Walk();
// method 2:
Dog obj2 = new Dog();
obj2.Walk();
// method 3:
Animal obj3= new Dog();
obj3.Walk();
Animal
继承中的构造方法 好多结论,考点!
1.知识点不重要,unity中很少写构造方法;》因为创建对象 常见在Start中初始化。 实例化,会拉到GameObject中。
2笔试中爱考【不好记,容易出错!】
- 构造方法不会继承给子类,
- 但是在创建子类对象时,自动调用父类的构造方法,
且父类构造方法先执行,子类构造方法后执行.【从子进入,再进入父,执行完父,执行子 结束】 - 什么时候调用构造方法:
new类时=创建对象的时候
new【1分配内存空间 ->2调用构造方法】 - 当子类创建对象时,默认调用父类的无参构造方法, 如果父类没有无参构造方法,则报编译错误。
解决方法有两个:
1.为父类添加无参构造方法。
2.在子类的构造方法中用base关键字指明要调用父类的哪一个有参构造方法
Public Dog() : base(“ssss”)
{
}
如果程序员没有明确的写构造方法,编译器或自动生成一个【无参构造方法】
如果程序员明确的写构造方法,编译器就不给生成了。
-
Q:1.为什么需要构造方法?
A obj=new A();1创建对象 【1分配内存空间,放对象;2初始化对象字段】 //A:
只要类型需要创建对象,都需要构造方法, 因为构造方法是创建对象的唯一通道. -
Q:2.为什么要重载构造方法?
A:构造方法在创建对象的同时,并为对象成员做初始化, 通过传递的参数为成员赋值。
当我们希望得到初始状态不同的对象时,需要使用重载构造方法 。
**比如:开新班有 unity的班, php的班, web前端的班, .net的班, Mis老师:准备一个教室,开新班!环境!
7. Q:3.构造方法之间可不可以互相调用 语法上:可以; 实际用途:几乎不用,没意义
//A:可以,本类构造方法互相调用通过this关键字, 子类调用父类的构造方法通过base关键字
哪种情况适合用继承组织类的关系:
[如果有多个类,概念一致,有很多共性 就可以考虑:继承]。
例如:动物,狗,猫
a.两个或更多个类从概念上是一致的
b. 从数据和行为上是一致的
c.会不会这几个类型统一处理
技巧:哪种情况适合用继承
举例:
[多个类, A B is成立,适合使用继承] 动物,狗
狗是动物,所以狗可以继承动物
Day3 多态
类的继承:
**类成员的横向扩展:**成员越来越多。
**类成员的纵向扩展:**行为改变,版本增高。
抽象类
c#抽象类可以包括有方法体的方法吗?
-不可以 抽象类当中必须是抽象的方法和属性,抽象的方法是不能有具体的方法体的,而且有抽象方法的类 。如果有其他类继承此类的话,那么此类就必须重写父类中的抽象方法。
-抽象类可以包含普通方法 但是抽象类不能被实例化。
总之:
1、抽象类中的抽象属性和抽象方法必须是公有的,因此必须有public修饰符
2、子类必须override抽象类中的所有抽象属性和抽象方法,如果没有全部override,那么子类必须是抽象类
3、抽象类中可以有非抽象属性和非抽象方法,也可以是私有或者公有,但是如果是私有的话子类就不能访问,无意义,所以一般情况下都设置为公有
4、有抽象方法或抽象属性的类一定是抽象类,抽象类中的属性或方法不一定都是抽象的
Timthy.Liu举得例子:
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApp2
{
class AbstractClassTest
{
static void Main()
{
Vehicle v = new Car();
v.Run();
v.Fill();
}
}
abstract class Vehicle
{
public void Stop()
{
Console.WriteLine("Stopped.");
}
public void Fill()
{
Console.WriteLine("Fill the gas.");
}
public abstract void Run();
}
class Car:Vehicle
{
public override void Run()
{
Console.WriteLine("the Car is running.");
}
}
class Truck : Vehicle
{
public override void Run()
{
Console.WriteLine("the Truck is running.");
}
}
class RaceCar : Vehicle
{
public override void Run()
{
Console.WriteLine("the RaceCar is running.");
}
}
}
重写(Override)成员方法
“hide方式”在实际项目中经常被视为一种错误。
-实际应用中,“hide方式”的 调用子类型中的方法是要尽量避免的。这种方式没什么意义,所以经常不用。而是用override
using System;
namespace ConsoleApp6
{
class Program
{
static void Main(string[] args)
{
Animal animal = new Animal();
Dog dog = new Dog();
dog.Sleep(5);// overload
dog.jump(); // override
}
}
class Animal
{
public virtual void jump()
{
Console.WriteLine("the animal jumps");
}
public void Sleep()
{
Console.WriteLine("Animal睡觉");
}
public int Sleep(int time)
{
Console.WriteLine("Animal {0}点睡觉", time);
return time;
}
}
class Dog: Animal
{
public override void jump()
{
Console.WriteLine("the dog jumps");
}
}
}
输出结果:
conclusion:如此可见,main函数中即使用父类Animal 声明的对象,因为实例化的是子类new Dog(),所以jump函数仍然调用子类的 jump功能,而自动把父类的jump给hide掉了。这就是多态。
多态:当用父类对象引用子类实例时,这个父类对象调用的方法永远是子类的方法,而且是继承链上最新版本的方法。
重写(Override)属性
using System;
namespace ConsoleApp6
{
class Program
{
static void Main(string[] args)
{
Animal animal = new Animal();
Dog dog = new Dog();
dog.Sleep(5);
dog.jump();
// test the property speed override
Animal animalDog = new Dog();
animalDog.jump();
Console.WriteLine("the speed is " + animalDog.speed);
}
}
class Animal
{
// property override
private int _speed ;
public virtual int speed
{
get { return _speed; }
set { _speed = value; }
}
public virtual void jump()
{
Console.WriteLine("the animal jumps");
_speed = 100;
}
public void Sleep()
{
Console.WriteLine("Animal睡觉");
}
public int Sleep(int time)
{
Console.WriteLine("Animal {0}点睡觉", time);
return time;
}
}
class Dog: Animal
{
private int rpm;//齿轮转速
public override int speed
{
get { return rpm / 100 ; }
set { rpm = value / 50; }
}
public override void jump()
{
Console.WriteLine("the dog jumps");
rpm = 50000;
}
}
}
接口:
一个纯abstract 类,其实就是接口:
TimothyLiu的例子如下:
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApp2
{
class AbstractClassTest
{
static void Main()
{
Vehicle v = new Car();
v.Run();
v.Fill();
}
}
interface IVehicleBase
{
void Stop();
void Fill();
void Run();
}
abstract class Vehicle:IVehicleBase
{
public void Stop()
{
Console.WriteLine("Stopped.");
}
public void Fill()
{
Console.WriteLine("Fill the gas.");
}
abstract public void Run();
}
class Car:Vehicle
{
public override void Run()
{
Console.WriteLine("the Car is running.");
}
}
class Truck : Vehicle
{
public override void Run()
{
Console.WriteLine("the Truck is running.");
}
}
class RaceCar : Vehicle
{
public override void Run()
{
Console.WriteLine("the RaceCar is running.");
}
}
}
//控制台会输出:the Car is running.
//Fill the gas.
接口为了解耦合而生
面向对象的编程的要点之一是高内聚,低耦合。所以引入接口,其目的:
1) 方便测试:代码越多,测试量越大。interface is without codes. 所以不需要测试其功能是否正确。
2) 降低耦合度,因为interface is without codes ,所以其对继承类的功能实现没有影响,降低了父类-子类的耦合度。
2) 功能提供方替代容易。
接口正是为起到了这个作用而诞生。请看如下实例:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApp2
{
class InterfaceTest2
{
static void Main()
{
var user = new PhoneUser(new Mi());
user.UsePhone();
}
}
class PhoneUser
{
private IPhone _phone;
public PhoneUser(IPhone phone)
{
_phone = phone;
}
public void UsePhone()
{
_phone.Dial();
_phone.Pickup();
_phone.Send();
_phone.Read();
}
}
interface IPhone
{
void Dial();
void Pickup();
void Send();
void Read();
}
class Nokia : IPhone
{
public void Dial()
{
Console.WriteLine("Nokia Dial");
}
public void Pickup()
{
Console.WriteLine("Nokia Pickup");
}
public void Send()
{
Console.WriteLine("Nokia Send");
}
public void Read()
{
Console.WriteLine("Nokia Read");
}
}
class Mi : IPhone
{
public void Dial()
{
Console.WriteLine("Mi Dial");
}
public void Pickup()
{
Console.WriteLine("Mi Pickup");
}
public void Send()
{
Console.WriteLine("Mi Send");
}
public void Read()
{
Console.WriteLine("Mi Read");
}
}
}
解读:
- 低耦合的实现:IPhone(手机)接口定义功能Dial,Pickup,Send,Read 与其派生类Nokia ,Mi 2款具体手机是低耦合的关系。也就是说,在实现Nokia ,Mi 2款具体手机的Dial,Pickup,Send,Read 功能时,并不依赖于IPhone(手机)接口中对于手机功能的任何定义。也就是你怎么改IPhone(手机)接口,对于Nokia ,Mi 2款具体手机的实现功能,是无影响的(低耦合)。
- 调用端的简化:在PhoneUser调用端,实例化一个具体的手机类 var user = new PhoneUser(new Mi());
依赖反转原则(dependency inversion)
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApp2
{
class Interface_DependencyInversion
{
static void Main()
{
DeskFan deskFan = new DeskFan(new PowerSupply());
Console.WriteLine(deskFan .Work());
}
}
public interface IPowerSupply
{
int GetPower();
}
public class PowerSupply:IPowerSupply //电源
{
public int GetPower()
{ return 260; } //这是一个非常不好的设计, 此处赋值(高耦合),未来测试会经常修改,这样会导致应用PowerSupply的其他类被影响。
}
public class DeskFan //台式电扇
{
private IPowerSupply _powerSupply; //台式电扇的电源参数。 这是一个紧耦合模式,
//因为PowerSupply中电压值改变,将导致Deskfan工作状态直接改变。
public DeskFan(IPowerSupply powerSupply) //初始化函数给台式电扇一个电源(即电压V)
{
_powerSupply= powerSupply;
}
public string Work()
{
int power = _powerSupply.GetPower();
if (power <= 0)
return "Won't work";
else if (0 < power && power < 100)
return "Low Power Work";
else if (100 <= power&&power <= 220)
return " Work Fine";
else
return "Warning : too high power";
}
}
}
解读:
类PowerSupply和类DeskFan 都继承了接口IPowerSupply
隐藏
方法隐藏
解决父类的方法在子类不适合的问题。
但是父类的方法不能重写(不是virtual,abstract,override),
此时可能选择方法隐藏,
子类新定义一个方法,与父类方法同签名,
子类型用新定义的方法隐藏掉父类继承的旧方法。
方法隐藏时语法建议在方法前加new关键字
Why:为了选择性的复用【解决父类的方法在子类不适合的问题】
What:隐藏【方法隐藏,方法隐藏技术,隐藏技术】
隐藏是多态的一种实现方式。
【使用步骤:】
在父类中定义普通的方法
在子类中定义隐藏方法
(用new修饰的和父类中的方法名相同参数相同的方法,
new可以省略,最好不省略)
虚方法
什么是虚方法:
定义【语法】:用vritual关键修饰的已经实现的方法,即是虚方法
语义【含义】:虚方法表示一个可以在子类中重写的方法
【重写不重写都可以】
方法重写
- 重写【方法重写,方法重写技术,重写技术】
- 重写有三种重写的方法
1》 虚方法重写
2》 重写方法重写
3》 抽象方法重写 - 为什么需要重写:为了选择性复用(解决父类所
定义方法在子类中不适用) - 虚方法重写 【定义,使用步骤 】
是多态实现的一种方式;
【步骤1:】在父类中定义虚方法
【步骤2:】在子类中定义重写方法
【重写方法:和父类中的
方法名相同参数相同方法返回类型相同,要加override】
注意对应关系!!
特点:调父的实现 1种写法;调子的实现 2种写法
父调子意义重大!
方法重写解决父类所定义方法在子类中不适用(虚方法),或父类没有实现(抽象方法),这样的方法子类可以重写,重写是为了满足子类对该方法的不同需求.
三种方法可以重写 :
a) abstract 方法在子类必须重写,除非子类也是抽象类
b) virtual 方法在子类可以重写,父类方法的做法与子类不同
c) override方法,已经重写过的方法,在子类还可以继续重写,除非被标识为sealed.
方法重写时必须在方法前加override关键字
sealed
封闭类定义:sealed修饰的类
特点:不能被继承。不能有子类。可以实例化!
sealed(密封)的作用 1放在类前 封闭类 2重写方法前 封闭方法
- 用在类的定义上,指示当前类不能做父类,也就是任何类都不可继承当前类
- 用在重写的成员,指示当前类的子类,不能再次重写该成员
sealed 用在方法前边,必须和override 在一起
sealed override 修饰的方法 叫封闭方法,只有重写方法才可以封闭。
动态绑定(晚期绑定)与静态绑定(早期绑定)[了解一下即可]
绑定:类型与关联的方法的调用关系,
通俗讲就是一个类型能够调用哪些方法 B:A A:?
【系统确定一个类型能调用哪些方法的过程,绑定】
1静态绑定(编译时绑定):是指调用关系是在运行之前确定的,即编译期间
2 动态绑定(运行时绑定):是指调用关系是在运行期间【过程中】确定的。
静态绑定因为在编译期确定,不占用运行时间,
所以调用速度比动态绑定要快
动态绑定因为在运行期确定,占用运行时间,但是更灵活。
动态绑定比静态绑定灵活性好. 速度略低
方法隐藏是静态绑定
方法重写是动态绑定
方法隐藏原理 了解
方法隐藏是子类的方法表加入一个新项,新项中表示的方法签名与继承来的方法签名一致,但是在方法表中不能出现相同签名的方法项,所以必须要隐藏掉一个。子类用新项隐藏旧项。方法隐藏是在方法表加新项
方法重写原理 了解
父类的方法在子类重写后,是子类型将方法表中相应方法签名对应的方法所在的地址重写了,重写后,原方法签名项关联到新方法的地址。当引用调用该方法时,访问新地址中的方法。
所以方法重写后,不管是通过父类还是子类型的引用,调用方法时,都调用对象真实类型中定义的方法
如:A是父类,B是子类,A中Show方法在子类中重写
A obj = new B(); //A类型引用指向B类型的对象
obj.Show();
此时调用的是B类型定义的Show方法.
什么时候使用抽象类,什么时候用接口
- 项目分析时,对某角色提取的是不同类别的共性,提取的有:特征,行为。其中,有的行为能确定下来;有的行为确定不下来。这种情况适用于抽象类『封装』;
- 提取的是不同类别的共性,提取的只有:行为。这些行为确定不下来,适用于接口『封装』
Day5 委托的应用
1. 委托作为参数传给调用函数:
将方法做为参数传递,可以将一个方法的执行代码注入到另一个方法中
方法1(int,string数据数值) Add(int a,int b)
方法2(方法1语句块)
定义 方法2(委托类型 委托对象){ 调用委托;…….}
调用 方法2(委托对象=lambda表达式)
为什么要注入:
1定义算法/方法时 :
有的语句/步骤能定下来,有的暂时定不下来
可以使用委托表示确定不下来的
2调用时再确定 把有些功能推迟到客户端【调用端】确定!
using System;
using System.Collections.Generic;
using System.Text;
namespace DemoDelegate5
{
class Delegate5
{
static void Main()
{
int a = 4, b = 2;
TestDelegateCallFun test = new TestDelegateCallFun();
Handler handler = test.ToBeCallFun;
test.CallFun(handler(a, b));// delegate transfer as a agument;
}
}
delegate int Handler(int x, int y);
class TestDelegateCallFun
{
public int ToBeCallFun(int x, int y)
{
return x + y;
}
public void CallFun(int x)
{
Console.WriteLine("total =" + x);
}
}
}
2.实现回调,实现的方式比接口更灵活
- 实现回调,实现的方式比接口更灵活
回调含义 1 方法 系统调用的 【不是程序员直接调用的】
含义 2 这里的含义
定义方法 有些定义时确定不下来,留给客户端确定
调用时候,从 调用端 到定义端
回到调用端 再到定义端执行 【灵活性】
回到调用端,结束
案例2: 回家 做事情【?复习,看电视,玩游戏,聊天】
方法1 方法2
唯一,能确定的 不确定的
进行程序设计,实现代码!
1》 接口可以作为方法的参数!
2》 委托可以作为方法的参数!
3》 有何不同?委托可以实现回调,作为参数,比接口更灵活