Лучший способ создать структуру данных (класс), которая может содержать только постоянное значение

Я создаю видеоигру, и в этой видеоигре есть разные классы. Каждый класс имеет ту же структуру данных, что и любой другой класс, но разные значения. Например, класс имеет 3 элемента: имя класса, начальные очки здоровья и путь к изображению класса.

Как лучше всего делать что-то подобное?

Лучше всего создать класс, а затем создать экземпляр для каждого игрового класса с соответствующим значением.

Пояснительный пример:

public class InGameClass {
    public string className;
    public int startingHP;
    public string picturePath;

    public InGameClass(string newClassName, int newStartingHP, string newPicturePath) {
        className = newClassName;
        startingHP = newStartingHP;
        picturePath = newPicturePath;
    }
}

И затем я создаю класс, который получает все экземпляры класса

public class InGameClasses {
    public InGameClass Mage = new InGameClass("Mage", 90, "Resources/Class/Mage");
    public InGameClass Warrior= new InGameClass("Warrior", 100, "Resources/Class/Warrior");
}

Во-первых, я бы рекомендовал не называть какую-либо концептуальную сущность в вашем проекте «классом». «Класс» в C# имеет очень конкретное значение, и его переопределение чем-либо еще никогда не может привести к путанице.

Fildor 17.08.2024 16:06
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
1
68
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вместо хранения имен классов вы можете создавать классы с этим именем, а затем сравнивать типы классов вместо их имен в виде строк.

public abstract class Character
{
    public abstract int StartingHP { get; }
    public abstract string PicturePath { get; }
}

public class Mage : Character
{
    public override int StartingHP => 90;
    public override string PicturePath => "Resources/Class/Mage"; 
}

public class Warrior : Character
{
    public override int StartingHP => 100;
    public override string PicturePath => "Resources/Class/Warrior"; 

    public void Attack(Character other); // Method specific to warriors.
    {
        ...
    }
}

Обратите внимание, что я создал базовый класс abstract. Это значит, что писать нельзя new Character(); однако вы можете, например, создать список персонажей, содержащий магов и воинов.

Производные классы вынуждены переопределить унаследованные абстрактные члены и предоставить конкретную реализацию.

List<Character> characters = new();
characters.Add(new Mage());
characters.Add(new Warrior());

Вы можете проверить тип персонажа следующим образом:

if (character is Warrior warrior) {
    warrior.Attack(someCharacter);
}

Поместите в базовый класс методы для того, что могут делать все персонажи. Например, public virtual void Walk() { ... }. Сделав его виртуальным, вы сможете переопределить его в классе-потомке, например. если персонаж ходит не так, как другие. Разные персонажи автоматически будут вести себя по-разному (см.: Полиморфизм).

Преимущество этого подхода (иерархии классов) заключается в том, что вы можете очень легко реализовать различное поведение для разных персонажей.

См. также:


Альтернативы (в ответ на комментарий):

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

Вы также можете использовать свойства только для получения, реализующие Статический абстрактный интерфейс. Это самая продвинутая техника. Он позволяет вам добавлять «константы» (реализованные как статические свойства, предназначенные только для получения) к каждому конкретному классу символов структурированным способом, т. е. путем применения определенного и четко определенного набора свойств.

Вы также можете сохранить структуру констант в словаре по типу символов.

Шаблон Абстрактная фабрика также может оказаться полезным.

Спасибо за ваш ответ. Дочерние элементы класса Персонаж должны представлять собой структуру данных, содержащую постоянные значения, поскольку это неизменяемые значения, которые применяются к каждому персонажу, например Магу или Воину. Есть ли способ сделать эти классы неизменяемыми или постоянными? Чтобы объяснить это лучше: могу ли я сделать что-то похожее на JSON, доступный только для чтения, который содержит всю общую информацию для игрового класса?

Francesco Laghetti 17.08.2024 19:22

Поэтому я могу просто получить информацию, не создавая экземпляр нового объекта для каждого нового персонажа.

Francesco Laghetti 17.08.2024 19:42

Хороший способ сделать это — использовать частный конструктор и статические значения в качестве «констант».

public class InGameClass 
{
    public string ClassName {get; private set; }
    public int StartingHP {get; private set; }
    public string PicturePath {get; private set; }

    private InGameClass(string newClassName, int newStartingHP, string newPicturePath) {
        ClassName = newClassName;
        StartingHP = newStartingHP;
        PicturePath = newPicturePath;
    }
    public static readonly InGameClass Mage;
    public static readonly InGameClass Warrior;

    static InGameClass()
    {
        Mage = new InGameClass("Mage", 90, "Resources/Class/Mage");
        Warrior= new InGameClass("Warrior", 100, "Resources/Class/Warrior");
    }
}

Здесь мы оставляем конструктор private, чтобы больше нельзя было создавать объекты, и используем статический конструктор для создания необходимых значений и сохранения их в полях static readonly.

Теперь вы можете использовать в своем коде InGameClass.Mage или InGameClass.Warrior.

Этот шаблон обычно называется типобезопасными перечислениями.

Другие вопросы по теме