目录
今天来详细学习一下C#面向对象的三大特性:封装、继承。
封装
封装(Encapsulation) 是面向对象编程(OOP)的一个基本特性,它指的是将对象的状态(字段)和行为(方法)捆绑在一起,并通过控制访问权限来保护对象的内部状态。封装的目的是实现数据隐藏,只允许通过公开的接口与对象进行交互,从而增强了数据的安全性、可维护性和代码的复用性。
1.核心思想
1)隐藏内部实现。对象的内部实现对外界是不可见的,外界只能通过暴露的公开接口与对象进行交互。
2)通过公共方法访问数据。常通过属性(Properties)和方法(Methods)来访问和操作对象的内部数据,而不是直接访问字段。
2.实现方式
封装在C#中通过使用访问修饰符和属性来实现。
C#提供了以下几种访问修饰符,用于控制类、字段、方法、属性等成员的访问权限。
public:公开成员,外部代码可以自由访问。
private:私有成员,仅能在当前类中访问。
protected:保护成员,派生类可以访问,外部无法访问。
internal:内部成员,只有在同一程序集(Assembly)内的代码可以访问。
protected internal:结合了protected和internal,表示派生类和同一程序集中的代码可以访问。
public class Person
{
//私有字段
private string name;
//公共属性,用于访问私有字段
public string Name
{
get { return name; }
set { name = value; }
}
//公共方法
public void DisplayName()
{
Debug.Log($"My name is {name}.");
}
}
3.优点
1)数据保护:通过封装,可以保护对象的内部状态不被外部直接修改。通过对属性的访问控制,可以确保数据的一致性和有效性。如,可以限制某个字段的值不能被非法修改,或者在设置字段值时添加一些验证逻辑。
2)代码重用:通过封装可以将常用的逻辑集中在类的内部,外部代码不需要关心这些实现细节,从而提高了代码的复用性。
3)降低耦合性:封装可以隐藏实现细节,使得外部代码只需要关注接口(方法、属性等),而不需要知道内部的实现。这降低了代码之间的依赖性(耦合度),提高了系统的灵活性和可维护性。
4)更易于维护:封装使得代码变得更加模块化。如果需要修改类的实现细节,只需修改类内部的代码,而外部的代码不需要修改,减少了维护的难度。
5)增强安全性:通过封装和使用访问修饰符,可以限制外部对某些敏感数据或功能的访问,提高系统的安全性。
继承
继承(Inheritance) 是面向对象编程(OOP)中的一个重要特性,它允许一个类从另一个类派生(继承)出新的类,从而实现代码的复用和扩展。通过继承,子类可以继承父类的属性和方法,并且可以对其进行扩展或修改。
1.语法
class A: B
表示A类继承B类,A类称为子类(派生类),B类称为父类(基类,超类)父类中所定义除私有成员外都继承给子类
2.特点
1)父类中除私有成员外、定义的都继承给子类,即子类拥有父类中所有非私有成员
2)构造方法不继承给子类
3. 为什么需要继承
1)为了能够对类进行层次性的管理
2)为了保留原有的功能,通过继承可以复用,不用每次都从头开始
例如,猫和狗都有吃食物的能力,就可以继承有Eat()方法的基类动物类Animal,在类Dog,Cat中就不需要再写一遍相同的方法了
public class Animal
{
public void Eat()
{
}
}
public class Dog : Animal
{
public void WangWang()
{
}
}
public class Cat : Animal
{
public void MiaoMiao()
{
}
}
4.继承中的构造方法
只要类型需要创建对象,都需要构造方法,因为构造方法是创建对象的唯一通道。
构造方法不会继承给子类,但是在创建子类对象时,自动调用父类的构造方法,且父类构造方法先执行,子类构造方法后执行。即从子类进入,再进入父类,执行完父类构造方法,再执行子类方法。
当子类采用无参构造方法创建对象时,默认调用父类的无参构造方法,如果父类没有无参构造方法,则报编译错误,原因:如果程序员没有明确的写构造方法,编译器或自动生成一个无参构造方法;如果程序员明确的写构造方法,编译器就不给生成了。
解决方法:
方法1:为父类添加无参构造方法
方法2:在子类的构造方法中用base关键字指明要调用父类的哪一个有参构造方法
using UnityEngine;
public class Test : MonoBehaviour
{
private void Start()
{
Animal obj1 = new Animal("动物");
obj1.Eat();
Dog obj2 = new Dog("小狗");
obj2.Eat();
Cat obj3 = new Cat("小猫");
obj3.Eat();
Cat obj4 = new Cat();
obj4.Eat();
}
}
public class Animal
{
public string name;
public Animal()
{
}
public Animal(string name)
{
this.name = name;
}
public void Eat()
{
Debug.Log(name + "吃东西");
}
}
public class Dog : Animal
{
public Dog(string name) : base(name)
{
}
public void WangWang()
{
}
}
public class Cat : Animal
{
public Cat()
{
}
public Cat(string name) : base(name)
{
}
public void MiaoMiao()
{
}
}
5.什么时候调用构造方法
new类时/创建对象的时候 。new做的事:①分配内存空间②调用构造方法
6.继承的优缺点
6.1优点
1)统一概念,概念复用
2)以层次化的方式管理类
3)复用代码的一种方式
6.2缺点
耦合度高。父类的改变直接影响到所有的子类,而不需要通知子类
7.注意
1)继承层次不要太深,三层左右即可
2)重载构造方法可以让构造方法在创建对象的同时,并为对象成员做初始化,通过传递的参数为成员赋值
3)构造方法之可以互相调用,本类构造方法互相调用通过this关键字,子类调用父类的构造方法通过base关键字
4)适合用继承组织类的关系的情况
①两个或更多个类从概念上是一致的
②从数据和行为上是一致的
③是否可以几个类型统一处理
8.抽象类
8.1语法
用abstract修饰类即为抽象类
public abstract class MyBaseClass
{
//抽象方法(没有实现)
public abstract void AbstractMethod();
//已实现的方法
public void ConcreteMethod()
{
Console.WriteLine("This is a concrete method");
}
}
注意:
1)抽象类不能创建对象
2)抽象类可以包含抽象方法(没有实现的方法),也可以包含已实现的方法(具有实现的普通方法)
8.2语义
抽象类表示一个抽象概念(数据和行为整体),表达概念中拥有的共性行为数据,定义统一的接口和行为规范。抽象只表示做什么,拥有什么数据,但往往不表达具体做法,不表达数据具体取值。用于做基类,统一所有子类,管理子类。
8.3抽象方法
1)语法
用abstract修饰并且没有实现的方法。只有方法声明,没有实现
抽象方法只能出现在抽象类中。抽象方法在本类中不实现,实现推迟到子类中,子类必须重写实现。
2)语义
抽象方法表达抽象行为,只关注本身,不关注行为实现。抽象方法描述做什么,不描述怎么做。抽象方法一个行为的抽象。定义一种规则,子类必须实现。
public abstract class Person
{
//抽象方法,子类必须实现
public abstract void Walk();
}
public class Man : Person
{
public override void Walk()
{
Debug.Log("走路");
}
}
8.4什么时候适合用抽象类
1)当需要表示一个抽象概念时
2)当不希望类创建对象的时候
3)当有行为,但是不需要实现的时候
4)当有多个概念,需要一致的管理时
5)当有一些行为,在做法上有多种可能时,但又不希望客户了解具体做法
8.5为什么尽量选择抽象类来继承
①强制子类遵循统一的接口和行为规范。
②抽象类不能被实例化,确保只能通过继承使用。
③提供代码复用的机会,让通用的逻辑集中在父类中。
④实现多态,增加程序的灵活性和可扩展性。
⑤便于未来的扩展和维护。
想要了解多态可查看【超详细】C#基础-面向对象三大特性:多态,好了,本次的分享到这里就结束啦,希望对你有所帮助~