【C#零基础从入门到精通】(十四)——C#封装详解
C# 是一门面向对象编程语言,面向对象编程语言有三大特性,分别是封装、继承和多态。所谓封装就是将一个或多个项目(函数)集合在一个单元中,这个单元称之为类,我们可以根据需要通过访问权限修饰符来设定类中成员的范围和可见性。
封装的概念
封装就像是一个黑盒子,把对象的属性和方法组合成一个独立的单元,对外界隐藏对象的具体实现细节,只暴露一些公共的接口。这样可以防止外部代码直接访问和修改对象的内部数据,从而提高代码的安全性和可维护性。例如,一个银行账户对象,账户余额是其内部数据,不应该被外部代码随意修改,而应该通过特定的方法(如存款、取款)来操作,这就是封装的体现。
实现封装的方式
1. 访问修饰符
访问修饰符用于控制类、属性、方法等成员的访问级别,C# 中常用的访问修饰符有以下几种:
public
:公共访问级别,被public
修饰的成员可以被任何类访问。private
:私有访问级别,被private
修饰的成员只能在定义它的类内部访问,外部类无法直接访问。这是实现封装的重要手段,通过将属性设置为private
,可以隐藏内部数据。protected
:受保护访问级别,被protected
修饰的成员可以在定义它的类及其派生类中访问。internal
:内部访问级别,被internal
修饰的成员可以在同一个程序集(通常是一个项目)内的任何类中访问。protected internal
:受保护内部访问级别,被protected internal
修饰的成员可以在同一个程序集内的任何类以及该类的派生类中访问。
2. 属性(Properties)
属性是一种特殊的成员,它提供了对私有字段的访问接口,允许在访问和修改字段值时进行额外的逻辑处理。属性通常包含 get
和 set
访问器:
get
访问器:用于获取属性的值。set
访问器:用于设置属性的值,在set
访问器中可以添加验证逻辑,确保属性值的合法性。
封装的优点
1. 数据隐藏和安全性
通过将数据成员设置为私有,并提供公共的属性或方法来访问和修改这些数据,可以防止外部代码直接访问和修改内部数据,从而提高数据的安全性。例如,在一个用户类中,用户的密码应该被封装起来,不允许外部代码直接访问和修改,只能通过特定的方法进行验证和修改。
2. 代码可维护性
封装使得类的内部实现细节对外部隐藏,当类的内部实现发生变化时,只要公共接口保持不变,外部代码就不需要进行修改。这降低了代码的耦合度,提高了代码的可维护性。
3. 提供统一的访问接口
属性和方法为外部代码提供了统一的访问接口,使得外部代码可以方便地使用类的功能,而不需要了解类的内部实现细节。
代码示例:
using System;
// 定义一个 Person 类
class Person
{
// 私有字段,用于存储姓名
private string name;
// 私有字段,用于存储年龄
private int age;
// 公共属性,用于访问和修改姓名
public string Name
{
get { return name; }
set
{
if (!string.IsNullOrEmpty(value))
{
name = value;
}
else
{
Console.WriteLine("姓名不能为空。");
}
}
}
// 公共属性,用于访问和修改年龄
public int Age
{
get { return age; }
set
{
if (value >= 0 && value <= 120)
{
age = value;
}
else
{
Console.WriteLine("年龄必须在 0 到 120 之间。");
}
}
}
// 公共方法,用于显示人员信息
public void DisplayInfo()
{
Console.WriteLine($"姓名: {Name}, 年龄: {Age}");
}
}
class Program
{
static void Main()
{
// 创建 Person 对象
Person person = new Person();
// 通过属性设置姓名和年龄
person.Name = "张三";
person.Age = 25;
// 调用方法显示人员信息
person.DisplayInfo();
// 尝试设置不合法的年龄
person.Age = 150;
// 再次显示人员信息
person.DisplayInfo();
}
}
代码解释
- 私有字段:
name
和age
是Person
类的私有字段,外部代码无法直接访问和修改它们。 - 公共属性:
Name
和Age
是公共属性,通过get
和set
访问器提供了对私有字段的访问接口。在set
访问器中添加了验证逻辑,确保姓名不为空,年龄在 0 到 120 之间。 - 公共方法:
DisplayInfo
是公共方法,用于显示人员的姓名和年龄信息。 - 外部调用:在
Main
方法中,通过属性设置和获取人员的姓名和年龄信息,并调用DisplayInfo
方法显示信息。当尝试设置不合法的年龄时,会输出相应的错误信息。
通过封装,Person
类的内部数据得到了保护,外部代码只能通过公共属性和方法来访问和操作这些数据,提高了代码的安全性和可维护性。
1、public
类中使用 public 访问权限修饰符修饰的成员变量或成员函数可以在其他函数和对象,我们可以从类的外部访问类中的公共成员(使用 public 修饰的类成员)。下面通过一个示例来演示一下:
using System;
namespace c.biancheng.net
{
class Demo
{
static void Main(string[] args){
Rectangle Obj = new Rectangle();
Obj.width = 5.5;
Obj.length = 3;
Obj.Display();
Console.ReadLine();
}
}
class Rectangle
{
// 成员变量
public double width, length;
public double GetArea(){
return width * length;
}
public void Display(){
Console.WriteLine("长方形的长:{0}", length);
Console.WriteLine("长方形的宽:{0}", width);
Console.WriteLine("长方形的面积:{0}", GetArea());
}
}
}
运行结果如下:
长方形的长:3
长方形的宽:5.5
长方形的面积:16.5
上面的示例中,成员变量 length 和 width 都是使用 public 修饰的,所以可以在 Main 函数中使用 Rectangle 类的实例来进行访问。
2、private
类中使用 private 访问权限修饰符修饰的成员变量或成员函数不能在其它函数或对象访问,即使是类的实例也不能访问这个类中的私有成员,只有同属一个类的函数才可以访问这个类的私有成员。
下面通过一个示例来演示一下:
using System;
namespace c.biancheng.net
{
class Demo
{
static void Main(string[] args){
Rectangle Obj = new Rectangle();
Obj.AcceptDetails();
Obj.Display();
Console.ReadLine();
}
}
class Rectangle
{
// 成员变量
private double width, length;
public void AcceptDetails(){
Console.WriteLine("请输入长方形的长度: ");
length = Convert.ToDouble(Console.ReadLine());
Console.WriteLine("请输入长方形的宽度: ");
width = Convert.ToDouble(Console.ReadLine());
}
public double GetArea(){
return width * length;
}
public void Display(){
Console.WriteLine("长方形的长:{0}", length);
Console.WriteLine("长方形的宽:{0}", width);
Console.WriteLine("长方形的面积:{0}", GetArea());
}
}
}
运行结果如下:
请输入长方形的长度:
12.3
请输入长方形的宽度:
23.4
长方形的长:12.3
长方形的宽:23.4
长方形的面积:287.82
上面的示例中,成员变量 length 和 width 使用 private 修饰,因此无法通过 Demo 类中的 Main 函数访问,只能通过 Rectangle 类中的成员函数 AcceptDetails() 和 Display() 来访问这些变量。又因为 Rectangle 中的成员函数 AcceptDetails() 和 Display() 是使用 public 修饰的,因此可以使用 Rectangle 类的实例 Obj 来调用它们。
3、protected
类中使用 protected 访问权限修饰符修饰的成员变量和成员函数可以在其子类中访问,也就是说基类(父类)中使用 protected 访问权限修饰符修饰的成员变量和成员函数可以在其子类中访问,这样有助于实现继承。关于继承我们将后面的章节详细介绍。
4、internal
类中使用 internal 访问权限修饰符修饰的成员变量和成员函数可以在当前程序集中的其他函数或对象中使用。换句话说就是,任何使用 internal 修饰的成员都可以被同一命名空间下的任何类或方法访问。
下面通过一个示例来演示一下:
using System;
namespace c.biancheng.net
{
class Demo
{
static void Main(string[] args){
Rectangle Obj = new Rectangle();
Obj.length = 33.3;
Obj.width = 22.2;
Obj.Display();
Console.ReadLine();
}
}
class Rectangle
{
// 成员变量
internal double width, length;
double GetArea(){
return width * length;
}
public void Display(){
Console.WriteLine("长方形的长:{0}", length);
Console.WriteLine("长方形的宽:{0}", width);
Console.WriteLine("长方形的面积:{0}", GetArea());
}
}
}
运行结果如下:
长方形的长:33.3
长方形的宽:22.2
长方形的面积:739.26
上面的示例中,没有使用任何访问权限修饰符修饰的成员函数 GetArea() 默认是私有的。
5、protected internal
类中使用 protected internal 访问权限修饰符修饰的成员可以在本类、派生类或者包含该类(使用 using 引入)的程序集中访问,在实现继承时也使用此方法。