2.1 变量与常量
想象你正在开发一个游戏,需要存储和管理各种数据:玩家的生命值会随着战斗而变化,游戏的基础伤害值保持固定,而一些配置在游戏启动时确定。C#提供了变量、常量和只读字段来满足这些不同的需求。
变量
变量就像游戏中的状态数据,它们的值可以随时改变。例如,玩家的生命值、经验值、位置等都需要使用变量来存储。
变量声明
// 基本语法:类型 变量名 = 初始值;
// 游戏相关的变量示例
int playerHealth = 100; // 玩家生命值
float moveSpeed = 5.0f; // 移动速度
string playerName = "勇者"; // 玩家名称
bool isGameOver = false; // 游戏是否结束
Vector3 position = new Vector3(); // 玩家位置
变量命名规则
- 必须以字母、下划线或@开头
- 可以包含字母、数字和下划线
- 区分大小写
- 不能使用C#关键字(除非使用@前缀)
// 正确的命名
int health;
float _damage;
string @class; // 使用@前缀可以使用关键字
int player1;
// 错误的命名
int 1player; // 不能以数字开头
float my-speed; // 不能包含连字符
string void; // 不能使用关键字
变量作用域
变量的作用域决定了它在代码中的可访问范围。
public class GameManager
{
private int _score = 0; // 类级别变量,整个类中都可访问
public void UpdateScore()
{
int bonus = 100; // 局部变量,只在方法内可访问
_score += bonus; // 可以访问类级别变量
if (_score > 1000)
{
string message = "新高分!"; // 块级变量,只在if块内可访问
ShowMessage(message);
}
// message 在这里不可访问
}
}
变量监控与常见错误
// 错误1:变量名重复
int playerHealth = 100;
{
int playerHealth = 80; // 编译错误:变量名重复
}
// 错误2:使用未初始化的变量
int damage;
int totalDamage = damage * 2; // 编译错误:使用未赋值的局部变量
// 错误3:类型不匹配
int health = "100"; // 编译错误:不能将字符串赋值给整数
// 错误4:超出范围
int maxHealth = int.MaxValue;
maxHealth += 1; // 运行时错误:整数溢出
常量
常量就像游戏中的固定配置,一旦设定就不能改变。例如,最大等级、基础伤害值、经验值系数等。
常量声明
public class GameConstants
{
// 游戏配置常量
public const int MAX_LEVEL = 100;
public const float BASE_DAMAGE = 10.0f;
public const string GAME_VERSION = "1.0.0";
public const float CRITICAL_MULTIPLIER = 2.0f;
}
只读字段
只读字段是一种特殊的变量,它的值只能在声明时或构造函数中设置,之后就不能改变。
public class GameSession
{
// 只读字段示例
public readonly string SessionId;
public readonly DateTime StartTime;
public readonly int Seed;
public GameSession()
{
// 在构造函数中初始化只读字段
SessionId = Guid.NewGuid().ToString();
StartTime = DateTime.Now;
Seed = new Random().Next();
}
}
变量、常量与只读字段的对比
特性 | 变量(var) | 常量(const) | 只读字段(readonly) |
---|---|---|---|
初始化时机 | 随时 | 声明时 | 声明时或构造函数中 |
可修改性 | 可修改 | 不可修改 | 仅构造函数中可修改 |
编译时确定 | 否 | 是 | 否 |
内存分配 | 堆/栈 | 编译时替换 | 实例成员 |
适用场景 | 游戏中变化的数据(血量、经验值) | 固定配置(最大等级、基础伤害) | 运行时确定的配置(随机种子、实例ID) |
最佳实践
public class GameConfig
{
// 1. 使用const定义编译时就能确定的常量
public const int MAX_LEVEL = 100;
public const float BASE_DAMAGE = 10.0f;
public const string GAME_VERSION = "1.0.0";
// 2. 使用readonly定义运行时才能确定的只读值
public readonly Guid GameId;
public readonly DateTime StartTime;
public readonly int RandomSeed;
// 3. 使用私有字段存储可变数据
private int _currentLevel;
private float _playerHealth;
private Dictionary<string, float> _damageMultipliers;
// 4. 使用属性提供受控的访问
public int CurrentLevel
{
get => _currentLevel;
set => _currentLevel = Math.Min(value, MAX_LEVEL);
}
public GameConfig()
{
// 初始化只读字段
GameId = Guid.NewGuid();
StartTime = DateTime.Now;
RandomSeed = new Random().Next();
// 初始化变量
_currentLevel = 1;
_playerHealth = 100;
_damageMultipliers = new Dictionary<string, float>();
}
}
实战练习
练习1:角色属性系统
实现一个角色属性系统,包含以下功能:
- 定义基础属性(生命值、攻击力、防御力等)
- 实现属性的计算(考虑装备加成、buff效果等)
- 实现等级提升机制
- 保存和加载角色属性
public class Character
{
// 常量定义
private const int MAX_LEVEL = 100;
private const float BASE_HEALTH = 100.0f;
private const float BASE_ATTACK = 10.0f;
private const float LEVEL_MULTIPLIER = 1.1f;
// 只读字段
public readonly string Id;
public readonly DateTime CreateTime;
// 基础属性
private int _level;
private float _health;
private float _attack;
private Dictionary<string, float> _equipmentBonuses;
private List<Buff> _activeBuffs;
// 属性访问器
public int Level => _level;
public float Health => CalculateFinalHealth();
public float Attack => CalculateFinalAttack();
public Character()
{
// 初始化只读字段
Id = Guid.NewGuid().ToString();
CreateTime = DateTime.Now;
// 初始化基础属性
_level = 1;
_health = BASE_HEALTH;
_attack = BASE_ATTACK;
_equipmentBonuses = new Dictionary<string, float>();
_activeBuffs = new List<Buff>();
}
// 等级提升
public void LevelUp()
{
if (_level >= MAX_LEVEL) return;
_level++;
_health = BASE_HEALTH * (float)Math.Pow(LEVEL_MULTIPLIER, _level - 1);
_attack = BASE_ATTACK * (float)Math.Pow(LEVEL_MULTIPLIER, _level - 1);
}
// 添加装备加成
public void AddEquipmentBonus(string slot, float value)
{
_equipmentBonuses[slot] = value;
}
// 添加Buff效果
public void AddBuff(Buff buff)
{
_activeBuffs.Add(buff);
}
// 计算最终属性
private float CalculateFinalHealth()
{
float total = _health;
// 计算装备加成
total += _equipmentBonuses.Values.Sum();
// 计算Buff效果
foreach (var buff in _activeBuffs)
{
total *= buff.HealthMultiplier;
}
return total;
}
private float CalculateFinalAttack()
{
float total = _attack;
// 计算装备加成
total += _equipmentBonuses.Values.Sum();
// 计算Buff效果
foreach (var buff in _activeBuffs)
{
total *= buff.AttackMultiplier;
}
return total;
}
}
// Buff类定义
public class Buff
{
public string Name { get; }
public float HealthMultiplier { get; }
public float AttackMultiplier { get; }
public DateTime ExpiryTime { get; }
public Buff(string name, float healthMult, float attackMult, TimeSpan duration)
{
Name = name;
HealthMultiplier = healthMult;
AttackMultiplier = attackMult;
ExpiryTime = DateTime.Now + duration;
}
public bool IsExpired => DateTime.Now > ExpiryTime;
}
练习2:游戏存档系统
实现一个游戏存档系统,要求:
- 使用合适的变量类型存储游戏数据
- 正确使用常量定义配置信息
- 使用只读字段保护关键数据
- 实现存档的保存和加载功能
public class GameSave
{
// 常量定义
private const string SAVE_FILE_EXTENSION = ".sav";
private const int MAX_SAVE_SLOTS = 10;
// 只读字段
public readonly string SaveId;
public readonly DateTime CreateTime;
public readonly string GameVersion;
// 游戏数据
private Character _player;
private List<Item> _inventory;
private Dictionary<string, bool> _achievements;
private GameSettings _settings;
public GameSave()
{
SaveId = Guid.NewGuid().ToString();
CreateTime = DateTime.Now;
GameVersion = GameConstants.GAME_VERSION;
_inventory = new List<Item>();
_achievements = new Dictionary<string, bool>();
_settings = new GameSettings();
}
// 保存游戏
public void Save(string slot)
{
if (int.Parse(slot) > MAX_SAVE_SLOTS)
throw new ArgumentException("无效的存档槽位");
var saveData = new SaveData
{
SaveId = this.SaveId,
CreateTime = this.CreateTime,
GameVersion = this.GameVersion,
Player = _player,
Inventory = _inventory,
Achievements = _achievements,
Settings = _settings
};
string json = JsonSerializer.Serialize(saveData);
string path = $"saves/save_{slot}{SAVE_FILE_EXTENSION}";
File.WriteAllText(path, json);
}
// 加载游戏
public static GameSave Load(string slot)
{
string path = $"saves/save_{slot}{SAVE_FILE_EXTENSION}";
if (!File.Exists(path))
throw new FileNotFoundException("存档文件不存在");
string json = File.ReadAllText(path);
var saveData = JsonSerializer.Deserialize<SaveData>(json);
var save = new GameSave
{
_player = saveData.Player,
_inventory = saveData.Inventory,
_achievements = saveData.Achievements,
_settings = saveData.Settings
};
return save;
}
}
// 存档数据结构
public class SaveData
{
public string SaveId { get; set; }
public DateTime CreateTime { get; set; }
public string GameVersion { get; set; }
public Character Player { get; set; }
public List<Item> Inventory { get; set; }
public Dictionary<string, bool> Achievements { get; set; }
public GameSettings Settings { get; set; }
}
总结
在本节中,我们学习了:
- 变量的声明、命名规则和作用域
- 常量的使用场景和特点
- 只读字段的应用
- 变量、常量和只读字段的对比
- 在游戏开发中的最佳实践
通过实战练习,我们实现了:
- 角色属性系统:展示了如何使用不同类型的变量管理游戏数据
- 游戏存档系统:展示了如何在实际项目中应用这些概念
在下一节中,我们将学习C#的基本数据类型。