Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Инкапсуляция в C#
Инкапсуляция это принцип объектно-ориентированного программирования, который предполагает скрытие деталей реализации и контроль доступа к данным класса. Это один из четырёх столпов ООП и критически важен для создания безопасного и поддерживаемого кода.
Основная идея
Инкапсуляция это упаковка данных и методов в единый класс с ограничением доступа к внутренним данным. Объект контролирует как другие объекты могут его использовать.
// Плохо: прямой доступ к данным
public class BadPlayer {
public int health = 100;
public int mana = 50;
}
var player = new BadPlayer();
player.health = -999; // Ой! Отрицательное здоровье
player.mana = 10000; // Ой! Бесконечная мана
// Хорошо: контролируемый доступ
public class GoodPlayer {
private int health = 100;
private int mana = 50;
public int GetHealth() => health;
public int GetMana() => mana;
public void SetHealth(int value) {
health = Mathf.Clamp(value, 0, 100); // Валидация
}
public void SetMana(int value) {
mana = Mathf.Clamp(value, 0, 50);
}
}
var player = new GoodPlayer();
player.SetHealth(-999); // Будет 0
player.SetMana(10000); // Будет 50
Модификаторы доступа
private — доступ только внутри класса
public class Account {
private decimal balance = 1000; // Скрыто от внешнего мира
// Только этот метод может менять баланс
public void Withdraw(decimal amount) {
if (amount <= balance) {
balance -= amount;
}
}
}
public — доступ отовсюду
public class Game {
public void StartGame() {
// Может быть вызвано откуда угодно
}
}
protected — доступ в классе и наследниках
public class Character {
protected int health = 100; // Доступно для наследников
protected virtual void TakeDamage(int damage) {
health -= damage;
}
}
public class Warrior : Character {
public override void TakeDamage(int damage) {
// Может использовать health
base.TakeDamage(damage);
}
}
internal — доступ внутри сборки
internal class DebugHelper {
// Доступно только внутри проекта
}
Свойства (Properties)
Свойства это более удобный способ контроля доступа к данным.
public class Player {
private int health = 100;
// Полное свойство
public int Health {
get { return health; }
set { health = Mathf.Max(0, value); }
}
// Короткое свойство (auto-property)
public string Name { get; set; }
// Только для чтения
public int Level { get; private set; }
// С валидацией
private int mana;
public int Mana {
get => mana;
set => mana = Mathf.Clamp(value, 0, 100);
}
}
var player = new Player();
player.Health = -50; // Будет 0
player.Name = "Hero";
player.Level = 5; // Ошибка! Это приватный setter
Зачем инкапсуляция?
1. Защита от неправильного использования
public class BankAccount {
private decimal balance;
public void Deposit(decimal amount) {
if (amount > 0) {
balance += amount;
}
}
public bool Withdraw(decimal amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
return true;
}
return false;
}
public decimal GetBalance() => balance;
}
// Нельзя прямо мутировать balance
var account = new BankAccount();
account.Deposit(100);
account.Withdraw(50);
// account.balance = -1000; // ОШИБКА! balance приватный
2. Добавление логики без изменения интерфейса
public class Player {
private int experiencePoints;
public int Experience {
get => experiencePoints;
set {
experiencePoints = value;
CheckLevelUp(); // Логика при установке
}
}
private void CheckLevelUp() {
if (experiencePoints >= 1000) {
LevelUp();
}
}
}
// Клиент не знает о CheckLevelUp
var player = new Player();
player.Experience = 1500; // Автоматически уровнуется
3. Контроль версионирования
// Версия 1
public class Inventory {
public List<Item> items; // public
}
// Версия 2: измени реализацию, интерфейс остаётся
public class Inventory {
private Dictionary<int, Item> items; // Внутри используем словарь
public List<Item> GetItems() => items.Values.ToList();
public void AddItem(Item item) => items[item.id] = item;
}
// Код клиента работает без изменений
var inventory = new Inventory();
var itemList = inventory.GetItems();
Примеры инкапсуляции в Unity
1. Система здоровья
public class HealthSystem : MonoBehaviour {
[SerializeField] private int maxHealth = 100;
private int currentHealth;
public event Action<int> OnHealthChanged;
private void Start() {
currentHealth = maxHealth;
}
public int GetHealth() => currentHealth;
public int GetMaxHealth() => maxHealth;
public float GetHealthPercent() => (float)currentHealth / maxHealth;
public void TakeDamage(int damage) {
currentHealth -= damage;
currentHealth = Mathf.Max(0, currentHealth);
OnHealthChanged?.Invoke(currentHealth);
if (currentHealth == 0) {
Die();
}
}
public void Heal(int amount) {
currentHealth += amount;
currentHealth = Mathf.Min(currentHealth, maxHealth);
OnHealthChanged?.Invoke(currentHealth);
}
private void Die() {
Debug.Log("Character died");
}
}
// Использование
var healthSystem = GetComponent<HealthSystem>();
if (healthSystem.GetHealth() > 0) {
healthSystem.TakeDamage(10);
}
2. Система инвентаря
public class Inventory {
private List<Item> items = new List<Item>();
public int MaxSlots { get; private set; } = 20;
public int GetItemCount() => items.Count;
public bool IsFull() => items.Count >= MaxSlots;
public bool AddItem(Item item) {
if (IsFull()) return false;
items.Add(item);
return true;
}
public bool RemoveItem(Item item) {
return items.Remove(item);
}
public Item GetItemAt(int index) {
if (index >= 0 && index < items.Count) {
return items[index];
}
return null;
}
public void Clear() {
items.Clear();
}
}
Рекомендации
Правило 1: По умолчанию private
public class Player {
// Приватные по умолчанию
private int health;
private int mana;
private string name;
// Открой только что нужно
public int GetHealth() => health;
public void TakeDamage(int damage) => health -= damage;
}
Правило 2: Используй свойства вместо getter/setter методов
// Плохо
public int GetHealth() { return health; }
public void SetHealth(int value) { health = value; }
// Хорошо
public int Health { get; set; }
public int Health { get; private set; }
Правило 3: Валидируй на входе
public class Weapon {
private int ammo;
public void SetAmmo(int value) {
ammo = Mathf.Max(0, value); // Валидация
}
}
Выводы
Инкапсуляция это контроль доступа к данным класса. Позволяет:
- Защитить данные от неправильного использования
- Добавлять логику без изменения интерфейса
- Менять реализацию без влияния на код клиентов
- Упростить поддержку и отладку кода
Всегда думай: какие данные должны быть приватными, какие публичными, и какие проверки нужны при доступе. Это основа хорошей архитектуры.