Могу ли я получить объект из коллекции, если есть один элемент, и список <объект>, если есть несколько элементов?

Я не уверен, что это можно сделать...

Я пытаюсь создать класс Dice и класс DiceSet в Unity. У DiceSet есть List<Dice>, но я хочу, чтобы DiceSet с одним кубиком был неявно эквивалентен Dice. Есть ли метод или функция, которая возвращает элемент, если он единственный элемент в списке, но в противном случае возвращает список? (Возможно, используя Linq или что-то в этом роде)

В настоящее время между Dice и DiceSet нет наследования, но я думаю, что если я заставлю Dice наследоваться от DiceSet и просто ограничу количество кубиков в наборе до 1, этого будет достаточно. Но мне также интересно, есть ли лучший способ сделать это. Я обеспокоен тем, что один Dice будет считаться DiceSet в ситуациях, когда этого не должно быть. Может быть, я слишком много думаю об этом.

Также было бы неплохо, если бы при сложении двух Dice получилось DiceSet. Хммм... Может быть, вместо DiceSet должно быть List<DiceSet>?

Обновлено: Дополнительная информация...

Я пытаюсь сделать Dice разборчивым из строки (например, «3d6+1» создаст DiceSet из трех Dice с шестью сторонами и с модификатором +1.

Что я думаю из ваших комментариев, я должен забыть о концепции «DiceSet» и просто сделать так, чтобы Dice содержал сам List<Dice>, и если он содержит только один кубик, это все равно просто Dice.

Ниже пример того, что я думаю сейчас. Правильно ли это мышление?


public class Dice : IDice, IRollable, IModifiable
{
    // Set containing quantities for each kind of dice
    public Dictionary<Dice, int> Set { get; set; }

    // implement IDice
    public int NumSides => SideValues.Count;
    public List<int> SideValues { get; private set; }

    // implement IModifiable
    public int Modifier { get; set; }
    public int ModifiedValue => RolledValue + Modifier;

    // implement IRollable
    public int RolledValue { get; protected set; }
    public void Roll() {
        RolledValue = SideValues[Random.Range(0, NumSides + 1)];
    }

    // Dice constructor
    public Dice(int sides = 6, int quantity = 1, int modifier = 0) {
        SideValues = new List<int>(Math.Max(sides, 1));
        for(int i = 1; i < NumSides + 1; i++) {
            SideValues.Add(i);
        }
        Modifier = modifier;
        AddDice(this, quantity);
    }

    // Adds dice to the set
    public void AddDice(Dice dice, int quantity){
        if (Set.ContainsKey(dice))
            Set[dice] += quantity;
        else
            Set.Add(dice, quantity);
    }

    // String conversions
    public override string ToString() {
        // ToDo
    }
    public void FromString(string diceString){
        // ToDo
    }
}

Если вам нужно вернуть изменяемое количество элементов, вы всегда должны вернуть какое-то перечисление (либо IEnumerable, либо Collection, List и т. д.). Если потребитель затем перебирает заданную последовательность, она может содержать ноль, один или несколько элементов.

Oliver 25.01.2023 15:19

@ Оливер, так ты говоришь, что нет, я не могу? Как насчет того, чтобы сделать DiceSet базовым классом для Dice? Как вы думаете, это сработает для того, что я пытаюсь сделать? Кроме того, если я единственный «потребитель», есть ли способ заставить его работать так, как мне хотелось бы?

Martin Bocanegra 25.01.2023 15:23

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

Oliver 25.01.2023 15:25

Итак, если DiceSet содержит один кубик, вы хотите, чтобы он неявно конвертировался в Dice? Как бы вы с этим работали, и насколько это было бы проще, чем проверка количества кубиков в DiceSet? Возможно, я что-то упускаю, но это звучит как ненужное усложнение.

dumetrulo 25.01.2023 15:26

@dumetrulo Я очень ценю это. Я могу думать об этом совершенно неправильно, но да, я изначально представлял DiceSet как обертку для List<Dice>. Но я думаю, что понимаю, что мне действительно нужны Dice : DiceSet и List<DiceSet>. Это верно?

Martin Bocanegra 25.01.2023 15:30

В моем дизайне у DiceSet есть коллекция Dice, вот и все. Нет наследства. Может быть, набор имеет некоторые методы или свойства для броска всех кубиков или возврата результатов всех бросков, в то время как у Dice есть методы или свойства для броска самого себя и возврата своего собственного результата.

Oliver 25.01.2023 15:50

@Oliver Да, именно так я изначально реализовал свой дизайн. Они оба реализуют интерфейс IRollable, и когда Roll() вызывается для DiceSet, он вызывает Roll() для каждого Dice в списке. Я хотел иметь возможность складывать два Dice вместе, чтобы получить DiceSet, и чтобы DiceSet можно было неявно преобразовать в Dice, но только если он имеет ровно один. Я хочу иметь возможность создать новый кубик с «1d6» и добавить «1d6+1», чтобы получить «2d6+1», или добавить «1d4-1», чтобы получить «1d6 + 1d4 — 1».

Martin Bocanegra 25.01.2023 16:02

Будет ли словарь более подходящим, чем список? Я мог бы ввести его по количеству сторон на кубике и получить кортеж, содержащий количество и модификатор. Затем мой метод Roll() будет получать случайное целое число для каждого ключа, для каждой величины. Однако мне пришлось бы полностью пересмотреть мой класс Dice, чтобы это сработало.

Martin Bocanegra 25.01.2023 16:05

Все еще не совсем понимаю, почему вам нужно неявно передать его в Dice ... почему бы не позволить обоим реализовать указанный интерфейс и не только иметь Roll, но и любой метод/свойство, которое должно поддерживаться обоими ... Я не понимаю вашего формулы есть, но похоже, что ваш IRollable может также иметь некоторое свойство геттера, и набор может обрабатывать как случаи с одним, так и с несколькими кубиками

derHugo 25.01.2023 18:57

@derHugo Да, я не знаю, о чем я думал изначально. Сейчас занимаюсь рефакторингом. В моем проекте гораздо больше интерфейсов и базовых классов, чем я описал в своем вопросе, поэтому их изменение занимает немного времени. Скоро выложу код.

Martin Bocanegra 26.01.2023 04:15

@Oliver Поскольку вы ответили на мой вопрос в своем первом комментарии, вы можете опубликовать его. Спасибо. Если у вас есть какие-либо другие предложения или указатели, я внимательно слушаю, но мне, вероятно, придется опубликовать другой вопрос, если мне понадобится дополнительная помощь.

Martin Bocanegra 26.01.2023 09:57
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
11
56
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

В зависимости от функциональности, которую должен предоставлять DiceSet, вы можете использовать Составной шаблон C#, чтобы клиенты, работающие с классом DiceSet, не знали, сколько Dice находится в DiceSet.

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

Martin Bocanegra 26.01.2023 10:00
Ответ принят как подходящий

В C# метод может возвращать только один тип. Также невозможно объявить два метода с одинаковым именем, одинаковыми параметрами и разными типами возвращаемого значения.

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

Второй случай соответствует вашей проблеме. Здесь вы должны вернуть тип коллекции. Затем вызывающий объект может проверить, содержит ли коллекция ноль, один или много элементов, и действовать соответствующим образом. Тип возвращаемой коллекции зависит от нескольких факторов. В зависимости от вашего варианта использования вы должны рассмотреть IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, IList<T>, ISet<T> или даже конкретный класс, такой как List<T> или HashSet<T>.

Обратите внимание, что особенно интерфейс IEnumerable<T> может оцениваться лениво. Это может привести к некоторым улучшениям производительности при разумном использовании или к ухудшению производительности, непреднамеренному поведению или странным эффектам при неправильном использовании.

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

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