想象一下,你在设计一款足球游戏。每个球员都有自己的属性,比如速度、射门、传球等等。这些属性就好像球员的“秘密档案”,决定了他们在球场上的表现。
但是,如果任何人都可以随意查看和修改这些“秘密档案”,那游戏就乱套了!
为了保护这些重要的数据,我们需要两个工具:
成员变量: 用来存储数据的“保险箱”。
访问修饰符: 用来控制谁能访问“保险箱”的“密码锁”。
基本概念
成员变量: 类中定义的变量,用来存储对象的属性。
访问修饰符: 控制成员变量访问权限的关键字。
访问修饰符
C# 中常用的访问修饰符有以下几种:
语法规则
声明成员变量的语法:
访问修饰符 数据类型 变量名;
游戏开发中的应用
在游戏开发中,成员变量和访问修饰符随处可见:
保护游戏数据: 使用
private
修饰符可以防止外部代码随意修改游戏数据,保证游戏逻辑的正确性。控制访问权限: 使用不同的访问修饰符可以控制不同代码块对数据的访问权限,提高代码的安全性。
实现封装: 将数据和操作封装在类中,只暴露必要的接口,可以降低代码的耦合度,提高代码的可维护性。
优点
数据安全: 访问修饰符可以限制对数据的访问,防止数据被意外修改。
代码清晰: 使用访问修饰符可以明确数据的访问权限,提高代码可读性。
易于维护: 封装可以降低代码的耦合度,方便代码修改和维护。
缺点
过度使用访问限制可能会增加代码的复杂度。
示例代码
// 定义一个“足球运动员”类
public class FootballPlayer
{
// 公有成员变量:姓名
public string name;
// 私有成员变量:速度、射门、传球
private int speed;
private int shoot;
private int pass;
// 构造函数
public FootballPlayer(string name, int speed, int shoot, int pass)
{
this.name = name;
this.speed = speed;
this.shoot = shoot;
this.pass = pass;
}
// 公有方法:展示球员信息
public void ShowInfo()
{
Console.WriteLine("姓名:" + name);
Console.WriteLine("速度:" + speed);
Console.WriteLine("射门:" + shoot);
Console.WriteLine("传球:" + pass);
}
}
// 创建一个球员对象
FootballPlayer player = new FootballPlayer("梅西", 95, 98, 92);
// 可以直接访问公有成员变量
Console.WriteLine(player.name); // 输出:梅西
// 不能直接访问私有成员变量
// Console.WriteLine(player.speed); // 会报错
// 可以通过公有方法访问私有成员变量
player.ShowInfo();
基础题目
创建一个“动物”类,包含名字(
public
)、年龄(private
)、体重(private
)等成员变量,以及吃饭、睡觉等方法。创建一个“手机”类,包含品牌(
public
)、型号(public
)、内存大小(private
)、存储空间(private
)等成员变量,以及打电话、发短信、玩游戏等方法。
进阶题目
设计一个简单的银行账户系统,使用类和对象表示账户,使用私有成员变量存储账户余额,并使用公有方法实现存款、取款等功能。
设计一个简单的学生管理系统,使用类和对象表示学生和课程,使用私有成员变量存储学生信息和课程信息,并使用公有方法实现添加学生、添加课程、查询成绩等功能。
形象解释
成员变量就像球员的“秘密档案”,存储着他们的关键信息。
访问修饰符就像“密码锁”,控制着谁能查看和修改这些“秘密档案”。
通过合理地使用成员变量和访问修饰符,你就能像一位尽职的卫士,保护好游戏中的重要数据!
protected、internal 和 protected internal:游戏代码的"权限门禁"
想象一下,你正在搭建一个乐高城堡,里面有国王、骑士和士兵。你想设置一些规则,控制谁能进入哪些区域:
国王拥有最高权限,可以进入任何地方。
骑士可以进入城堡的大部分区域,但不能进入国王的宝库。
士兵只能进入指定的区域,比如城墙和训练场。
在编程世界里,我们也需要类似的规则来控制代码的访问权限,这就是 protected
、internal
和 protected internal
的作用。
基本概念
protected
(受保护的): 想像成城堡里的“骑士”。受保护的成员就像骑士可以进入的区域,只能被类自身和它的子类访问,即使子类位于不同的程序集中。internal
(程序集内部的): 想像成城堡的“城墙”。内部成员就像城墙内的区域,只能被同一个程序集内的代码访问,其他程序集无法访问。protected internal
(受保护的内部): 想像成城堡里的“骑士训练场”。 这些成员可以被同一个程序集内的代码访问,也可以被任何程序集中的子类访问。
语法规则
声明成员变量和方法时,在数据类型前面加上访问修饰符即可:
c
复制
// 受保护的成员变量
protected int health;
// 内部方法
internal void DoSomething() { }
// 受保护的内部属性
protected internal string PlayerName { get; set; }
游戏开发中的应用
protected
: 主要用于继承关系中,允许子类访问父类的某些成员,同时对其他代码隐藏这些成员。例如,游戏角色的移动逻辑可以定义为protected
方法,允许子类(如战士、法师)继承并修改移动方式。internal
: 用于控制代码库的可见性,将一些辅助类或工具函数限制在特定的程序集中,避免外部代码直接使用。例如,游戏引擎的一些内部工具函数可以使用internal
修饰,防止游戏逻辑代码直接调用。protected internal
: 较少使用,通常用于框架或库开发中,允许同一个程序集内的代码和不同程序集中的子类访问特定成员。
优点
封装性: 控制代码的访问权限,隐藏实现细节,降低代码耦合度。
安全性: 防止外部代码随意访问和修改内部数据,提高代码的健壮性。
可维护性: 模块化的代码结构更易于理解、修改和扩展。
缺点
过度使用访问限制可能会增加代码的复杂度。
示例代码
// 程序集 A
public class Enemy
{
protected int health = 100;
internal void TakeDamage(int damage)
{
health -= damage;
}
}
// 程序集 B
public class Boss : Enemy
{
public void SpecialAttack()
{
// 可以访问父类的 protected 成员
health -= 50;
}
}
public class Player
{
public void Attack(Enemy enemy)
{
// 无法直接访问 enemy 的 internal 方法
// enemy.TakeDamage(20); // 会报错
// 需要通过其他方式调用
DamageEnemy(enemy, 20);
}
internal void DamageEnemy(Enemy enemy, int damage)
{
enemy.TakeDamage(damage);
}
}
基础题目
创建一个“动物”基类,包含一个
protected
的MakeSound()
方法,然后创建几个子类(如狗、猫),重写MakeSound()
方法发出不同的声音。创建一个包含两个类的程序,一个类包含
internal
方法,尝试在另一个类中调用该方法,观察是否能够成功。
进阶题目
设计一个简单的游戏框架,使用
protected
成员变量存储游戏状态,并使用internal
方法控制游戏流程。创建一个包含多个程序集的游戏项目,尝试使用不同的访问修饰符控制代码的可见性和访问权限。
形象解释
protected
就像城堡里的“骑士通行证”,只有骑士和他们的继承者可以使用。internal
就像城堡的“城门钥匙”,只有城内的人才能拥有。protected internal
就像城堡里的“骑士训练邀请函”,城内的人和骑士的后代都可以参加。