1、结构的使用 结构(struct):一次性声明多个不同类型的变量
声明的位置:将结构声明到命名空间的下面,类的外面,标识这个命名空间下,所有的类 都可使用这个结构
初始化结构成员可通过两种方式来完成
一是:使用参数化构造函数,即使用new关键字
二是:在声明结构后分别访问成员(如果只给部分字段复制会报错)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _1_结构的使用
{
//最好将结构声明在此位置,因为所有的类都可以访问
public struct Clerk//构造函数的声明
{
//声明变量
public string name;
public int age;
public string department;
public char gender;
}
class Program
{
//如果把结构声明在这个位置,只有当前类或其继承类才能使用
static void Main(string[] args)
{
//构造方法的使用
Clerk zs = new Clerk();
zs.name = "张三";
zs.age = 25;
zs.department = "采购部";
zs.gender = '男';
Console.WriteLine(zs.name+"\t"+zs.age + "\t" + zs.department + "\t" + zs.gender);
Console.ReadKey();
}
}
}
2、枚举 如果希望得到一个固定集合的值,就采用枚举
声明方式
【public】 enum 枚举名
{
值1
值2
值3
……
}
声明位置:将枚举声明到命名空间的下面,类的外面,表示这个命名空间下所有的类 都可使用这个枚举
枚举就是一个变量类型,int doubule string decimal
只是枚举声明、赋值、使用的方式跟哪些普通的变量类型不一样,通过**“枚举名.”**的方式引用
默认情况下,每个值都会根据定义的顺序从0开始,自动赋予每个值一个整型。
2.1 类型转换 枚举与int相互转换,要获得枚举的值,只要展缓为sting类型。
枚举与string相互转换
如果将字符串转换成枚举类型则需要下面这样一行代码:
- (要转换的枚举类型)Enum.Parse(typeof(要转换的内聚类型),“要转换的字符串”)
- 如果转换的字符串是数字,则就算枚举中没有,也不会抛异常
- 如果转换的字符串是文本,如果枚举中没有,则会抛出异常
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _2_枚举
{
//在此处声明枚举,它与结构作用类似,所以位置一样,但同时,枚举也可以在结构中被调用
public enum Gender
{
男,
女
}
public enum department
{
//每个值都会根据定义的顺序从0开始,自动赋予每个值一个整型。
人力资源部,//0
财务部,//1
测试部,//2
研发部,//3
产品部=10,//此处是认为指定了一个整数,而不再是默认的顺序了
总裁部}
class Program
{
static void Main(string[] args)
{
//枚举的调用
Gender zsgender = Gender.男;
Console.WriteLine(zsgender);
Console.WriteLine((int)zsgender);
//将枚举转换为整型
Console.WriteLine(zsgender.ToString());
//将枚举转换为字符串
department zsdepartment = department.财务部;
Console.WriteLine(zsdepartment);
Console.WriteLine((int)zsdepartment);
//此处输出为3,
department lsdepartment = department.产品部;
Console.WriteLine(lsdepartment);
Console.WriteLine((int)lsdepartment);
//此处输出为10,
int myint = 10;
Console.WriteLine((department)myint);
//将整型转换为枚举,此处输出是产品部//将枚举转换为字符串,不能用(string),只能用.Tostring或convert.Tostring()
Console.WriteLine(lsdepartment);
//在输出时,输出语句已自动将枚举类型转换为字符串乐行
Console.WriteLine(lsdepartment.ToString());
Console.WriteLine(Convert.ToString(lsdepartment));
//将字符串转换为枚举值
string mystr = "产品部";
Console.WriteLine((department)Enum.Parse(typeof(department), mystr));
string mystr2 = "产品部部";
Console.WriteLine((department)Enum.Parse(typeof(department), mystr2));
//此处会报异常
Console.ReadKey();
}
}
}
3、面向对象(oop——Object Oriented Programming)
- 面向过程:将一个项目(或者一件事)从头到尾按顺序,一步一步完成,先做什么,后做什么,一直到结束,也是我们人做事的方法。
- 面向对象:将一个项目(或者一件事)分成更小的项目(或者更小的部分),每一部分负责一方面的功能,最后由这些部分组成一个整体。这种方法更适合多人工作。
- 面向过程(Procedure Oriented)是分析解决问题的步骤,然后用函数把这些步骤一步一步的实现,然后再使用的时候一一调用则可。强调的是完成这件事的动作,更接近我们日常处理事情的思维
- 面向对象(Object oriented),把构成问题的事务分成各个对象,而建立对象的目的也不是为了完成一个个比欧洲,而是为了描述某个事物在解决整个问题的过程中所发生的的行为。意在写出通用代码,加强代码重用,屏蔽差异性。
- 现实世界中我们描述一个对象是通过描述其特征和行为说明的
- 我们在代码中描述一个对象,通过描述这个对象的属性(特征)和方法(行为)。对象必须是看的见摸得着的。
- 我们把这些具有相同属性和相同方法的对象进行进一步分账,抽象出来类这个概念
- 类就是一个模子,确定了对象应该具有的属性和方法。
- 对象是根据类重建出来的。类不占内存,对象站内存。对象是相互独立的,对其中一个对象操作,不会影响同类下的另一个对象。
- 类是一种数据结构,可以包含数据成员(常量和字段)函数成员(方法、属性、时间、索引器、运算符、实例构造函数、静态构造函数、析构函数)以及嵌套类型。
- C#中一切类型都为类,除(引用的命名空间外)所有的语句都必须位于类(或者结构)内,不存在任何处于类之外的语句。因此类是C#语言核心和基本构成模块。默认代码中包含一个Program类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _5_类的声明
{
//声明一个类
class clerk
{}class Program
{
static void Main(string[] args)
{
}
}
}
类的声明一般不用上面方法声明而是:(快捷键 shift+alt+c)
文章图片
文章图片
文章图片
5.2 类的赋值 类:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _5_类的声明
{
//枚举
public enum Gender
{
男,
女
}
class clerk
{
//方法中可以存放字段,可以存放属性,还可以存放方法//在类中声明的变量称为字段
//变量只能存放一个值,字段可以存放多个值,字段是用来存放数据的
//字段的命名规范:_cameCase(下划线非必须)
public string _name;
public Gender _gender;
public int _age;
public string _department;
public int _workYear;
//静态方法只能调用静态成员,非静态方法可以调用任何成员
publicvoid Write()
{Console.WriteLine("我叫{0},我是{1}生,我{2}岁了,我在{3}任职,我工作了{4}年", _name, _gender, _age, _department, _workYear);
}
public void Write2()
{//this代表实例化的对象Console.WriteLine("我叫{0},我是{1}生,我{2}岁了,我在{3}任职,我工作了{4}年", this._name, this._gender, this._age, this._department, this._workYear);
}}
}
Main方法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _5_类的声明
{
//声明一个类
class clerk1
{}class Program
{
static void Main(string[] args)
{
//将类实例化, 就是将类指定给某个对象
clerk zsClerk = new clerk();
zsClerk._name = "张三";
zsClerk._gender = Gender.男;
zsClerk._department = "人力部";
zsClerk._age = 40;
zsClerk._workYear = 25;
string mystring = "张三";
//调用非静态方法
zsClerk.Write();
//再次实例化
clerk lsClerk = new clerk();
lsClerk._name = "李四";
lsClerk._age = 30;
lsClerk._gender = Gender.女;
lsClerk._workYear = 3;
lsClerk._department = "财务部";
mystring = "李四";
lsClerk.Write();
lsClerk.Write2();
//此命令行说明this关键字//以下输出是说明字段可以存放多个值,变量只能存放一个值
Console.WriteLine(zsClerk._name);
Console.WriteLine(lsClerk._name);
Console.WriteLine(mystring);
Console.WriteLine(mystring);
Console.ReadKey();
}
}
}
6、属性的使用
- 使程序员可以创造新的声明性信息的种类,称为属性。属性是对显示世界中实体特征的抽象,是为访问自定义类型的注释信息提供通用访问方式。属性使能够以一种公开的的思路方法
- 属性的作用:保护字段,对字段的取值与复制进行限定
- 说明:属性往往被声明在字段的下方,并且将字段私有化;
- 属性格式:
public 【对应字段类型】 属性名
{
get {return _name}
set{ _name=_value; }
} - 有了属性以后,我们往往都会通过属性来访问字段(打个比方说:属性是男人,负责外部事务,字段是女人负责内部事务)
- 属性通常声明为public,字段声明private()
- 在外部访问类中的字段,都是通过属性来实现的
- 属性的“设置”(Set)方法和“获取”(Get)方法饱汉子属性声明中,通常我们将get与set称为访问器。Get属性访问器用于返回属性值,Set属性访问器用于分配新值
- 属性分为4种
- 既读又写 同时包含get与set
- 只读,只包含get
- 只写 只包含set
- 自动 get;set;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _6_属性
{
class Clerk
{
//类中可以存放的成员
//字段:采用_camelClass命名方式
//属性:采用PascalCase命名方式
//方法
private string _name;
public string Name
{
//自动属性,主要是为了预留,以便以后限定属性
get;
set;
}
private char _gender;
//有了属性以后,我们往往都会通过属性来访问字段
//打个比方说:属性是男人,负责外部事务,字段是女人负责内部事务
//属性通常声明为public,字段声明private()
//在外部访问类中的字段,都是通过属性来实现的
//通常我们将get与set称为访问器
//属性分为4种
// 1)
public char Gender
{
get //get可以用于对取值进行限定 _age
{
if (_gender != '男' || _gender != '女') _gender = '男';
return _gender;
}
set//set 可以用于对赋值进行限定 value
{ _gender = value;
}//value是属性里面内置的一个变量,不能人为改变的名称
}
private int _age;
public int Age
{get
{
return _age;
}
set
{
if (value < 0 || value > 120) value = https://www.it610.com/article/0;
// 限定赋值
_age = value;
}
}
private string _department;
public string Department
{
get;
set;
}
public int _workYear;
public int WorkYear
{
get;
set;
}
public void Wrte()
{
Console.WriteLine("我叫{0},我是{1}生,我{2}岁了,我在{3}任职,我工作了{4}年", this.Name, this.Gender, this.Age, this.Department, this.WorkYear);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _6_属性
{
class Program
{
static void Main(string[] args)
{
//将类实例化,并分别赋值,并调用其中的方法
Clerk zsClerk = new Clerk();
zsClerk.Name = "张三";
zsClerk.Age = 25;
zsClerk.Department = "人力部";
zsClerk.WorkYear = 5;
zsClerk.Gender = '男';
zsClerk.Wrte();
Console.ReadKey();
}
}
}
8、构造函数和析构函数 构造函数和析函数共性:编写代码时,如果没有提供它们,则编译器自动添加
8.1 构造函数 构造函数是一个特殊的方法
- 构造函数没有返回值,连void也不能写,必须public
- 构造函数的名称必须跟类名一样
作用:帮助我们初始化对象(给对象的每个属性一次的赋值)
创建对象的时候会执行构造函数,构造函数是可以重载的
类当中会有一个默认的无参数的构造函数,当你写一个新的构造函数之后,不管是由参数还是无参数的,那个默认的无参数的构造函数都被覆盖掉。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _8_构造函数和析构函数
{
public enum Gender
{
男,
女
}
class Clerk
{
private string _name;
public string Name
{
get;
set;
}
private Gender _gender;
public Gender Gender
{
get
{
return _gender;
}set
{
_gender = value;
}
}
private int _age;
public int Age
{
get
{
return _age;
}set
{
_age = value;
}
}
private string _department;
public string Department
{
get
{
return _department;
}set
{
_department = value;
}
}
private int _workYear;
public int WorkYear
{
get
{
return _workYear;
}set
{
_workYear = value;
}
}
public void Write()
{
Console.WriteLine("我叫{0},我是{1}生,我{2}岁了,我在{3}任职,我工作了{4}年", this.Name, this.Gender, this.Age, this.Department, this.WorkYear);
}
//构造函数
public Clerk(string name, Gender gender, int age, string department, int workYear)
{
this.Name = name;
this.Age = age;
this.Gender = gender;
this.Department = department;
this.WorkYear = workYear;
}public Clerk(string name, Gender gender, int age) //构造函数重载
{
this.Name = name;
this.Age = age;
this.Gender = gender;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _8_构造函数和析构函数
{
class Program
{
static void Main(string[] args)
{
//Clerk zsClerk = new Clerk();
//zsClerk.Name = "张三";
//zsClerk.Gender = Gender.男;
//zsClerk.Age = 30;
//zsClerk.Department = "测试部";
//zsClerk.WorkYear = 10;
//zsClerk.Write();
Clerk zsClerk = new Clerk("张三",Gender.男,25,"测试部",5);
zsClerk.Write();
Clerk lsClerk = new Clerk("张三", Gender.男, 25);
lsClerk.Write2();
Console.ReadKey();
}
}
}
8.3 new关键字
- 在内存中开辟空间
- 在开辟的空间中创建对象
- 对对象进行初始化,江哥哥属性值赋值
- 析构函数是实现销毁一个类的实例的方法成员,析构函数不能有参数,不能有任何修饰符而且不能被调用。析构函数与构造函数目的不同,特在析构函数前加上前缀“~”以示区别
- 构造函数与析构函数虽然是一个类中形式上比较简单函数,但他们使用绝非看上去那么简单,因此灵活而正确地使用构造函数与析构函数能够帮助用户更好地理解CLR的内存机制以及更好地利用系统资源
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _8_构造函数和析构函数
{
public enum Gender
{
男,
女
}
class Clerk
{
private string _name;
public string Name
{
get;
set;
}
private Gender _gender;
public Gender Gender
{
get
{
return _gender;
}set
{
_gender = value;
}
}
private int _age;
public int Age
{
get
{
return _age;
}set
{
_age = value;
}
}
private string _department;
public string Department
{
get
{
return _department;
}set
{
_department = value;
}
}
private int _workYear;
public int WorkYear
{
get
{
return _workYear;
}set
{
_workYear = value;
}
}
public void Write()
{
Console.WriteLine("我叫{0},我是{1}生,我{2}岁了,我在{3}任职,我工作了{4}年", this.Name, this.Gender, this.Age, this.Department, this.WorkYear);
}
public void Write2()
{
Console.WriteLine("我叫{0},我是{1}生,我{2}岁了", this.Name, this.Gender, this.Age);
}
//构造函数
public Clerk(string name, Gender gender, int age, string department, int workYear)
{
this.Name = name;
this.Age = age;
this.Gender = gender;
this.Department = department;
this.WorkYear = workYear;
}public Clerk(string name, Gender gender, int age) //构造函数重载
{
this.Name = name;
this.Age = age;
this.Gender = gender;
}//如果系统中没有指定析构函数,那么编译器有GC来决定什么时候释放资源
//Garage Collection
~Clerk()//析构函数
{
Console.WriteLine("我是析构函数,看我什么时候调用");
//在使用完当前类的时候,会调用析构函数
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _8_构造函数和析构函数
{
class Program
{
static void Main(string[] args)
{
//Clerk zsClerk = new Clerk();
//zsClerk.Name = "张三";
//zsClerk.Gender = Gender.男;
//zsClerk.Age = 30;
//zsClerk.Department = "测试部";
//zsClerk.WorkYear = 10;
//zsClerk.Write();
Clerk zsClerk = new Clerk("张三",Gender.男,25,"测试部",5);
zsClerk.Write();
Clerk lsClerk = new Clerk("张三", Gender.男, 25);
lsClerk.Write2();
Console.ReadKey();
}
}
}
10、类的继承 面向对象的三个基本特征是封装、继承、多态
实例:三个类:
Clerk: _name, Name; _department, Department 可以声明为父类
Sales:_name, Name; _department, Department, _salesTarget,SalesTarget 声明为子类,继承Clerk
Technical Support:_name, Name; _department, Department; _satisfactionRate, SatisfactionRate 声明为子类,继承Clerk
- 继承:对于类而言,所谓的继承,就是子类包含父类的数据结构和行为方式,包括字段、属性、方法和事件
- 尽管在子类本身的定义中没有包含这些定义,但仍可以使用这些父类成员。
- 在类的继承中,被继承的类叫基类或父类,继承的类叫派生类或子类
- 当一个类从另一个类派生出来时,派生类就自然具有基类数据成员、属性成员和方法成员等,基类定义中这些成员的代码,已不需要在派生类定义中重写,在派生类的定义中,只需要编写基类定义中所布局有的代码即可。
- 目的:
- 提高了代码的重用性
- 提高程序设计的效率
- 为程序设计中的特别需要提供了编写代码的自由空间,从而提高了已有程序设计成果的可扩展性
- 类击沉注意规则:
- 单根型(只能继承一个类)
- 传递性(查看类图)
文章图片
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _10_类的继承
{
class clerk
{private string _name;
//private是私有,所以不允许被子类访问
public string Name
{
get { return _name;
}
set { _name = value;
}
}
private string _department;
public string Department
{
get
{
return _department;
}set
{
_department = value;
}
}
public void CSayHello()
{
Console.WriteLine("大家好我是{0}的{1}",this.Name, this.Department);
}}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _10_类的继承
{
//继承Clerk类
class sales:clerk
{
private int _salesTarget;
public int SalesTarget
{
get
{
return _salesTarget;
}set
{
_salesTarget = value;
}
}
public void SSayHello()
{
Console.WriteLine("大家好,我是{0}的{1},我的销售目标是{2}",this.Department, this.Name, this.SalesTarget);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _10_类的继承
{
class TechnicalSupport:clerk
{
private double _statisfactionRate;
public double StatisfactionRate
{
get
{
return _statisfactionRate;
}set
{
_statisfactionRate = value;
}
}
public void TSSayHello()
{
Console.WriteLine("大家好,我是{0}的{1},我的服务满意率为{2}",this.Name,this.Department,this.StatisfactionRate);
}
}
}
- 派生类定义与基类同名的成员,则覆盖基类成员
文章图片
文章图片
- 派生类自然继承类的成员,但不能继承基类的构造函数成员 解决此问题有两种方法:
- 在父类中定义一个无参构造函数,或在子类中定义自己的构造函数
- 使用关键字:base()
文章图片
文章图片
文章图片
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _10_类的继承
{
class Program
{
static void Main(string[] args)
{
clerk zsclerk = new clerk("张三", "人事部");
sales zssales = new sales("张三", "人事部",2500);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _10_类的继承
{
class clerk
{private string _name;
//private是私有,所以不允许被子类访问
public string Name
{
get { return _name;
}
set { _name = value;
}
}
private string _department;
public string Department
{
get
{
return _department;
}set
{
_department = value;
}
}
public void CSayHello()
{
Console.WriteLine("大家好我是{0}的{1}",this.Name, this.Department);
}//构造函数
//子类中不能继承父类中的构造函数,但会默认第调用父类中的无参构造函数
//两种方法子类继承父类的构造函数
//1)在父类中再写一个无参的构造函数,在没个子类当中都需要进行一次构造函数的重写与各个字段的赋值
//2) 使用关键字:base()public clerk(string name, string department)
{
this.Name = name;
this.Department = department;
}
public clerk() { }}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _10_类的继承
{
//继承Clerk类
class sales:clerk
{privatestring _name;
//new关键字可以用来隐藏基类中同名成员
//new实例化对象
public new string Name
{
get
{
return _name;
}set
{
_name = value;
}
}private int _salesTarget;
public int SalesTarget
{
get
{
return _salesTarget;
}set
{
_salesTarget = value;
}
}
public void SSayHello()
{
Console.WriteLine("大家好,我是{0}的{1},我的销售目标是{2}",this.Department, this.Name, this.SalesTarget);
}
//子类构造函数方式一
//public sales(string name, string department, int salesTarget)
//{
//this.Name = name;
//this.Department = department;
//this.SalesTarget = salesTarget;
//}
//子类构造函数方式而
public sales(string name, string department, int salesTarget):base(name,department)
{
this.SalesTarget = salesTarget;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _10_类的继承
{
class TechnicalSupport : clerk
{
private double _statisfactionRate;
public double StatisfactionRate
{
get
{
return _statisfactionRate;
}set
{
_statisfactionRate = value;
}
}
public void TSSayHello()
{
Console.WriteLine("大家好,我是{0}的{1},我的服务满意率为{2}", this.Name, this.Department, this.StatisfactionRate);
}public TechnicalSupport(string name, string department, double satisfactionRate) : base(name, department)
{
this.StatisfactionRate = satisfactionRate;
}
}
}
10.1 类的密封 如果用户希望一个类不被作为基类使用,那么久须使用sealed关键字
唯一的限制是抽象类不能作为封闭的类使用,因为抽象类的本质决定它们必须被作为基类使用。
封闭类的作用是防止意外的派生操作。具体地说,因为编译器确定这个类没有任何派生类,所以可以将封闭类实例上的虚拟函数成员调用转换为非虚拟调用。
文章图片
文章图片
11、类的封装
- 封装是实现面向对象程序设计的第一步,封装就是将数据或函数等集合在一个单元中(我们称之为类)。被封装的对象通常被称为抽象数据类型
- 意义:封装的意义在于防止代码(数据)被我们无意中破坏。防止对实现细节的访问。
- 我们只提供嗲用类的方法,而调用者不必了解到类内部怎样处理相关数据
- C#中通常将方法或者其他数据成员封装在一个类中,
- 具体封装是使用访问修饰符来实现。一个访问修饰符定义了一个类成员的范围和可见性
- 访问修饰符:
- public
- private
- protected
- internal
- protected internal
- public:公开的,允许一个类将其成员变量和成员函数暴露给其它的函数和对象。任何共有成员可以被外部的类访问
- private:只能当前类的内部访问,类成员的默认访问修饰符。允许一个类将其成员变量和成员函数对其它的函数和对象进行隐藏。只有同一个类中的函数可以访问它的私有成员。即使是类的实例也不能访问它的私有成员
- protected:受保护的,只能当前类内部及子类中访问,允许子类访问它的基类的成员变量和成员函数。遮掩有助于实现继承。
- internal只能在当前项目中访问。在同一个项目中,internal和public的权限是一样的。允许一个类将其成员变量和成员函数暴露给当前程序中的其他函数和对象。换句话说,带有internal访问修饰符的任何成员可以被定义在该成员所定义的应用程序内的任何类或方法访问。
- Protected internal:允许一个类将其成员变量和成员函数对统一应用程序内的子类意外的其他的类对象和函数进行隐藏。这也被用于实现继承
- 能够修饰类的访问修饰符只有两个:public、internal
- 访问性质不一致,子类的访问权限不能高于父类的访问权限
- 在类的继承中,C#允许在基类与派生类中声明具有同名的方法,而且同名的方法可以有不同的代码,也就是说在基类与派生类的相同功能中可以有不同的实现方法,从而解决同一问题提供多种途径
- 多态性就是指在程序运行时,执行的虽然是一个调用方法的语句,却可根据派生类对象的类型不同完成方法不同的具体实现。
- 虚方法:将父类的方法标记为虚方法,使用关键字virtual,此方法在子类中可以重写(使用关键字override)
- 【C#|九、C#结构 类 属性】抽象类与抽象方法:如果我们不需要使用父类创建对象,它的存在只是为共子类继承。可以将父类写成抽象类(关键字abstract)类,将父类方法写成抽象方法,子类中的方法仍用关键字override重写
- 接口实现
我们选择使用虚拟方法实现多态还是抽象类抽象方法实现多态,取决于我们是否需要使用基类实例化的对象
- 抽象类:不需要使用基类实例化对象
- 虚方法:需要使用基类实例化对象
文章图片
12.2虚拟方法实现多态 main方法调用
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _13类的多态
{
class Program
{
static void Main(string[] args)
{
//虚方法实现多态
clerk myclerk = new clerk();
projectManager mypm = new projectManager();
clerk[] clerks = { myclerk, mypm };
foreach (clerk outclerk in clerks)
{
outclerk.WorkPlan();
}foreach (clerk outclerk in clerks)
{
outclerk.WorkPlan2();
}Console.ReadKey();
}
}
}
基类及虚方法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _13类的多态
{
class clerk
{
public void WorkPlan()
{
Console.WriteLine("我是职员,我需要工作计划");
}
public virtual void WorkPlan2()
{
Console.WriteLine("虚方法下,职员的工作计划");
}}
}
派生类及虚方法重写
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _13类的多态
{
class projectManager:clerk
{
public new void WorkPlan()
{
Console.WriteLine("我是项目经理,我也需要工作计划");
}
public override void WorkPlan2()
{
Console.WriteLine("虚方法下,项目经理工作计划");
}}
}
文章图片
抽象类抽象方法实现多态 Main方法
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _13类的多态
{
class Program
{
static void Main(string[] args)
{
//抽象方法实现多态(抽象类是不允许创建实例的)
Drink myMilk = new Milk();
Drink myTea = new Tea();
Drink[] drinkArry = { myMilk, myTea };
foreach (Drink outDrink in drinkArry) outDrink.drink();
Console.ReadKey();
}
}
}
基类(抽象类)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _13类的多态
{
abstract class Drink//抽象类
{
//里用抽象来实现,类抽象化,方法抽象化,并且方法中不能有方法体{}
public abstract void drink();
//抽象方法,抽象方法不能包含方法体
//{
//Console.WriteLine("我是饮料我可以解渴");
//}
}
}
派生类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _13类的多态
{
class Milk:Drink
{
public override void drink()
{
Console.WriteLine("我是牛奶,我可以解渴");
}
}
}
派生类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _13类的多态
{
class Tea:Drink
{
public override void drink()
{
Console.WriteLine("我是茶,我可以解渴");
}
}
}
总结 在使用类与对象的情况下,很少再使用结构
推荐阅读
- C#|C# 文件路径操作
- C# 接口实例
- C#|10、接口、抽象、密封、开放封闭原则
- c#|11、C#处理程序异常的技术
- C#|c# HashtableTo Json 字符串 HashtableToWxJson
- 分表分库(百亿级大数据存储)
- C#|微信小程序开发系列(六)——“处理请求时出错”怎么处理()
- c#做的一个简单的包含实时进度的进度条
- asp.net|c#文件写入与获取post请求数据