C-sharp面向对象
面向对象两个重要概念:类和对象
面向对象三大特征:封装、继承和多态
类和对象
例子,我们定义一个Person类代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace demo { internal class Person // 类首 { // 类体 //成员变量 public string name; public int age; //成员方法 public void Speak() { Console.WriteLine("我是{0},我今年{1}岁", name, age); } } }
然后在main方法中使用这个类,即在Program.cs文件中输入代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace demo { class Program { static void Main(string[] args) { Person person = new Person(); // 实例化对象 person.name = "tom"; person.age = 19; person.Speak(); Console.ReadLine(); } } }
类的成员
类是一种引用数据类型。类的成员有:
构造方法
构造方法名和类名相同
构造方法没有返回值类型
构造方法名前的权限修饰符通常使用public
构造方法的重载 ,例子:
在Person类中代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace demo { internal class Person // 类首 { // 类体 //成员变量 public string name; public int age; //成员方法 public Person() // 定义无参构造方法 { Console.WriteLine("无参构造方法调用了"); } public Person(string name) // 定义有参构造方法 { Console.WriteLine("有参构造方法调用了,name={0}",name); } public void Speak() { Console.WriteLine("我是{0},我今年{1}岁", name, age); } } }
在Program.cs中编写代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace demo { class Program { static void Main(string[] args) { Person person1 = new Person(); // 实例化对象(无参构造方法) Person person2 = new Person("tom"); // 有参构造方法 Console.ReadLine(); } } }
属性
属性是一种与当前对象关联的、主要用于访问类中某一数据成员 的机制
为了防止数据被其他人随意修改,把字段声明为私有访问权限(封装性 )
属性包括属性头和属性体。属性头(属性名),属性体(get
和set
)
例子,在Person类中输入代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace demo { internal class Person // 类首 { // 类体 //声明字段 private string name; private int age; // 声明属性 // 通过快捷键自动生成的get和set public string Name { get => name; set => name = value; } public int Age { get => age; set => age = value; } //public string Name //{ // get { return name; } // 读操作 // set { name = value; } // 写操作 //} //public int Age //{ // get { return age; } // set // { // if (age<0) // { // Console.WriteLine("年龄输入不合法!"); // } // else // { // age = value; // } // } //} //// 自动属性 //public char Gender { get; set; } //成员方法 public void Speak() { Console.WriteLine("我是{0},我今年{1}岁", Name, Age); } } }
其中快速生成get和set的快捷键为Ctrl R E
,自动生成的代码中的=>
是Lambda表达式,可自行查看。
关于C#自动属性可自行查看。
属性和方法都属于类的方法成员 ,都可以完成对类体中数据成员的访问。不同之处为:
属性名后不需要()
,方法名后必须有()
属性不能指定参数,方法可以指定参数
属性不能使用void类型,方法可以使用void类型
修饰符
访问修饰符(C# 编程指南) ,C#的类修饰符和成员修饰符
类的常用修饰符:
public 表示访问权限不受限制
abstract 表示这是一个抽象类
sealed 表示这是一个密封类(不能被继承)
类的成员常用修饰符:
public 表示公有成员,访问不受限制
private 表示私有成员,只能在成员所在类内访问。(类成员的默认访问权限)
protected 表示受保护成员,只能在成员所在类内和子类访问
static 表示静态成员变量,只能使用类名访问该成员。不用static修饰的成员称为实例成员,需要使用类的实例(即对象)进行访问
继承
菜鸟教程
继承是通过已有类创建新类的机制。
被继承的已有类称为超类(super class,又称基类、父类)
继承得到的新类称为扩展类(expansion class,又称子类、派生类)
在C#的继承中:
例子,我们先定义一个Person父类,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace demo02 { class Person { // 类体 //声明字段 private string name; private int age; // 声明属性 // 通过快捷键自动生成的get和set public string Name { get => name; set => name = value; } public int Age { get => age; set => age = value; } //成员方法 public void Speak() { Console.WriteLine("我是{0},我今年{1}岁", Name, Age); } } }
然后创建Student类,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace demo02 { class Student:Person // Student类继承了Person类 { } }
然后在main方法中输入代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace demo02 { class Program { static void Main(string[] args) Student student = new Student(); student.Name = "tom"; student.Age = 19; student.Speak(); Console.ReadLine(); } } }
可以看到Student类以及可以继承Person类的成员变量和方法了。
关于构造方法的继承,例子如下:
先在父类Person中添带参构造方法如下:
1 2 3 4 5 6 public Person(string name,int age) { Name = name; Age = age; Console.WriteLine("调用了父类有参构造函数"); }
然后在子类Student类中添加代码如下:
1 2 3 4 5 6 7 class Student:Person // Student类继承了Person类 { public Student(string name,int age) : base(name, age) { Console.WriteLine("调用了子类的有参构造函数"); } }
然后在main方法中执行代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace demo02 { class Program { static void Main(string[] args) { Student student = new Student("tom",19); student.Speak(); Console.ReadLine(); } } }
结果为:
(具体可自行查看)
抽象类和抽象方法
使用abstract
关键字作为修饰符的类和方法即为抽象类和抽象方法
抽象类本身不能被实例化,即不能直接创建抽象类的对象
抽象类只能作为父类,用于继承
当某个抽象类被继承得到的子类作为非抽象类后,该子类可以正常进行实例化操作
抽象方法只有方法头,没有方法体。即没有方法的具体实现
每个抽象方法的方法声明以分号;
结束
抽象类和抽象方法的关系:
抽象类可以不包含抽象方法
如果某个类中包含了抽象方法,则该类必须声明为抽象类
当继承抽象类的子类是非抽象类时,则必须在子类中为抽象父类中的抽象方法提供具体的实现
在实现抽象方法时,需要在方法头使用override
关键字
例子:
我们声明一个抽象类Animal,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace demo03 { abstract class Animal { public abstract void Bark(); // 抽象方法 public void Sleep() // 非抽象方法 { Console.WriteLine("睡着了"); } } }
然后在创建其子类Dog,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace demo03 { class Dog : Animal { public override void Bark() // 实现抽象方法 { Console.WriteLine("狗再叫"); } } }
(在子类中重写抽象方法快捷键:Alt+Enter
)
执行代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace demo03 { class Program { static void Main(string[] args) { Dog dog = new Dog(); dog.Bark(); Console.ReadLine(); } } }
sealed密封类
sealed(C# 参考)
sealed
关键字来修饰类、实例方法和属性作用如下:
类的声明使用sealed修饰,表示该类不允许被继承
方法声明使用sealed修饰,表示方法重写了父类的方法,但不允许再被其子类重写
属性声明使用sealed修饰,表示属性重写了父类的属性,但不允许再被其子类重写
接口
菜鸟教程
接口是完全的抽象成员的集合,即接口体中只能包含抽象成员(抽象方法、抽象属性等)
接口默认的成员默认为公有、抽象成员,因此接口的成员声明前不能添加 public
和abstract
关键字
一个类可以实现多个接口,接口的实现使用冒号:
使用interface
关键字声明接口
(单继承多实现)
例子:
创建一个接口ISleepable,代码如下:(接口命名一般在前面加I)
1 2 3 4 5 6 7 8 9 10 11 12 13 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace demo04 { interface ISleepable { void Sleep(); } }
然后创建People类来实现ISleepable接口,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace demo04 { class PeoPle : ISleepable { public void Sleep() { Console.WriteLine("人可以睡觉"); } } }
执行代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace demo04 { class Program { static void Main(string[] args) { People people = new People(); people.Sleep(); Console.ReadLine(); } } }
再来一个例子 :有三类人员教师、学生和程序员,他们都有姓名、年龄等属性,都可以通过Sleep方法睡觉,其中教师可以教学。
我们分别创建Teacher、Student、Coder类,并让其继承父类People。
People类代码为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace demo04 { class People : ISleepable { private string name; private int age; public string Name { get => name; set => name = value; } public int Age { get => age; set => age = value; } public void Sleep() { Console.WriteLine("{0}睡觉了",Name); } } }
创建ITeach接口,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace demo04 { interface ITeach { void Teach(); } }
创建Teacher类代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace demo04 { class Teacher : People, ITeach // 继承People类,实现ITeach接口 { public void Teach() { Console.WriteLine("{0}开始教课了", Name); } } }
然后分别创建Student类和Coder类,关键部分代码如下:
1 2 3 4 5 6 namespace demo04 { class Student:People { } }
1 2 3 4 5 6 namespace demo04 { class Coder:People { } }
执行代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace demo04 { class Program { static void Main(string[] args) { Teacher teacher = new Teacher(); Student student = new Student(); Coder coder = new Coder(); teacher.Name = "老师"; student.Name = "学生"; coder.Name = "程序员"; teacher.Sleep(); teacher.Teach(); // 实现了ITeach接口,即可以调用Teach方法 student.Sleep(); coder.Sleep(); Console.ReadLine(); } } }
例子3:两个接口中的抽象方法同名的情况
创建两个接口IMyInterface1和IMyInterface2,关键部分代码如下:
1 2 3 4 5 6 7 namespace demo04 { interface IMyInterface1 { void Print(); } }
1 2 3 4 5 6 7 namespace demo04 { interface IMyInterface2 { void Print(); } }
然后创建MyClass类,来实现这两个接口,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace demo04 { class MyClass : IMyInterface1, IMyInterface2 { void IMyInterface1.Print() { Console.WriteLine("实现了IMyInterface1中的Print方法"); } void IMyInterface2.Print() { Console.WriteLine("实现了IMyInterface2中的Print方法"); } } }
(Alt+Enter
,选择显示实现接口 )
然后执行代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace demo04 { class Program { static void Main(string[] args) { IMyInterface1 interface1 = new MyClass(); // 多态 IMyInterface2 interface2 = new MyClass(); // 多态 interface1.Print(); interface2.Print(); Console.ReadLine(); } } }
多态
菜鸟教程
多态是同一个行为具有多个不同表现形式或形态的能力。
多态性 意味着有多重形式。在面向对象编程范式中,多态性往往表现为"一个接口,多个功能"。
多态性可以是静态的或动态的。在静态多态性 中,函数的响应是在编译时发生的。在动态多态性 中,函数的响应是在运行时发生的。
在 C# 中,每个类型都是多态的,因为包括用户定义类型在内的所有类型都继承自 Object。
(多态:子类对象父类声明 )
多态主要表现为:方法重载和方法重写(override)
方法重载:在一个类中包含名字相同 但参数类型不同 的多个方法
方法重写:如果父类提供的功能不能满足要求,则可以在子类中重新定义
在父类中如果想让某个方法被子类重写,可以使用virtual
关键字将方法声明为虚方法,然后就可以在子类中使用override
关键字重写该方法
在C#中方法默认是非虚拟的即不能被重写(若要重写需要virtual
关键字)
virtual
不能和static
同时使用
virtual
不能和private
同时使用
重写方法的名称、参数、类型、返回值必须与原虚方法完全一样
例子:创建一个父类Animal,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace demo05 { class Animal { public virtual void Bark() { Console.WriteLine("动物在叫"); } } }
然后创建其子类Dog类和Cat类,关键部分代码如下:
1 2 3 4 5 6 7 class Dog:Animal { public override void Bark() { Console.WriteLine("狗在叫"); } }
1 2 3 4 5 6 7 class Cat:Animal { public override void Bark() { Console.WriteLine("猫在叫"); } }
执行代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace demo05 { class Program { static void Main(string[] args) { // 里氏替换原则 Animal animal1 = new Dog(); // 多态 animal1.Bark(); Animal[] arrAnimla = { new Dog(), new Cat()}; for (int i = 0; i < arrAnimla.Length; i++) { arrAnimla[i].Bark(); } //// 不使用多态 //Animal animal = new Animal(); //Dog dog = new Dog(); //Cat cat = new Cat(); //animal.Bark(); //dog.Bark(); //cat.Bark(); Console.ReadLine(); } } }