Не получается получить доступ к члену (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 шт):

Автор решения: Igor Borzenkov

Ваши два класса связаны отношением наследования. Это означает, что экземпляр класса Human получает доступ к членам класса с видимостью как public, так и protected. Но методы/поля/свойства, помеченные, как protected, он видит только свои собственные. Т.е., например, допустим код:

protected override void AttackTo(Person enemy)
{
    // .....
    this.HitPoint = enemy.HitPoint - tempDamage;
    // .....
}

Но в вашем случае, на вход метода AttackTo приходит другой экземпляр класса Person. Для этого экземпляра применяются все правила, как для внешнего, "незнакомого" объекта. Т.е. в методе AttackTo доступны только публичные члены класса Human.

В вашем случае, если есть необходимость менять значения свойств базового класса, можно сделать их публичными. Это наиболее простое решение.

Следует помнить, что способ организации доступа к полю через Reflection весьма не дёшев и затратный и применять его следует достаточно осмотрительно.

→ Ссылка