Не получается получить доступ к члену (protected) базового класса
Не получается изменить значение поля hitPoint или свойства HitPoint через дочерний класс. Хотел через обычную код:
enemy.HitPoint -= tempDamage; -- Выдает ошибку (Human класс)
Базовый класс Person
public abstract class Person
{
// ===== Поля класса =====
// Раса персонажа.
private string race;
// Количество урона.
protected int damage;
// Количество жизней.
protected double hitPoint;
// Шанс на критический удар.
protected double critChance;
// Количество единиц защиты.
protected int defense;
// ===== Свойства класса =====
public string Race { get => this.race; }
public int Damage
{
get => this.damage;
protected set => this.damage = value;
}
public double HitPoint
{
get => this.hitPoint;
protected set
{
if (value > this.hitPoint)
{
value = this.hitPoint;
}
this.hitPoint = value;
}
}
public double CritChance
{
get => this.critChance;
protected set => this.critChance = value;
}
public int Defense
{
get => this.defense;
protected set => this.defense = value;
}
// ===== Конструктор класса =====
/// <summary>
/// Создание персонажа.
/// </summary>
/// <param name="damage"> Урон. </param>
/// <param name="hitPoint"> Количество жизней. </param>
/// <param name="critChance"> Шанс критического урона. </param>
/// <param name="defense"> Единица защиты. </param>
/// <param name="race"> Раса. </param>
public Person(int damage, double hitPoint, double critChance, int defense, string race)
{
// Проверка на соответствие условий.
if (damage < 10 || damage > 40)
{
throw new ArgumentException(
"Количество урона должно быть в пределах от 10 до 40 (включительно)",
nameof(damage));
}
if (hitPoint < 100 || hitPoint > 600)
{
throw new ArgumentException(
"Количество жизней должно быть в пределах от 100 до 600 (включительно)",
nameof(hitPoint));
}
if (critChance < 0 || critChance > 0.5)
{
throw new ArgumentException(
"Шанс на критический удар должен быть в пределах от 0 до 0.5 (включительно)",
nameof(critChance));
}
if (defense < 0 || defense > 5)
{
throw new ArgumentException(
"Количество единиц защиты должно быть в пределах от 0 до 5 (включительно)",
nameof(defense));
}
// Присвоение значений полям.
this.damage = damage;
this.hitPoint = hitPoint;
this.critChance = critChance;
this.defense = defense;
}
// ===== Методы класса =====
/// <summary>
/// Атаковать противника.
/// </summary>
/// <param name="enemy"> Противник. </param>
public void Attack(Person enemy)
{
AttackTo(enemy);
}
protected abstract void AttackTo(Person enemy);
}
Унаследованный класс Human
public class Human : Person
{
public Human(int damage, double hitPoint, double critChance, int defense)
: base(damage, hitPoint, critChance, defense, "Human") { }
protected override void AttackTo(Person enemy)
{
Random rnd = new Random();
var tempDamage = rnd.NextDouble() > this.critChance ? this.damage : this.damage * 1.15;
enemy.GetType()
.GetField("hitPoint", BindingFlags.NonPublic | BindingFlags.Instance)
.SetValue(enemy, enemy.HitPoint - tempDamage);
Console.WriteLine($"У врага осталось {enemy.HitPoint} HP.");
//enemy.HitPoint -= tempDamage; -- Ошибка
}
}
Ответы (1 шт):
Ваши два класса связаны отношением наследования. Это означает, что экземпляр класса Human получает доступ к членам класса с видимостью как public, так и protected. Но методы/поля/свойства, помеченные, как protected, он видит только свои собственные.
Т.е., например, допустим код:
protected override void AttackTo(Person enemy)
{
// .....
this.HitPoint = enemy.HitPoint - tempDamage;
// .....
}
Но в вашем случае, на вход метода AttackTo приходит другой экземпляр класса Person. Для этого экземпляра применяются все правила, как для внешнего, "незнакомого" объекта. Т.е. в методе AttackTo доступны только публичные члены класса Human.
В вашем случае, если есть необходимость менять значения свойств базового класса, можно сделать их публичными. Это наиболее простое решение.
Следует помнить, что способ организации доступа к полю через Reflection весьма не дёшев и затратный и применять его следует достаточно осмотрительно.