Я не уверен, что это можно сделать...
Я пытаюсь создать класс 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
}
}
@ Оливер, так ты говоришь, что нет, я не могу? Как насчет того, чтобы сделать DiceSet базовым классом для Dice? Как вы думаете, это сработает для того, что я пытаюсь сделать? Кроме того, если я единственный «потребитель», есть ли способ заставить его работать так, как мне хотелось бы?
Создание DiceSet с одним кубиком, а затем всегда возвращать DiceSet кажется подходящим. Как видите, ваш набор представляет собой оболочку набора игральных костей, поэтому он будет таким же, как я сказал.
Итак, если DiceSet
содержит один кубик, вы хотите, чтобы он неявно конвертировался в Dice
? Как бы вы с этим работали, и насколько это было бы проще, чем проверка количества кубиков в DiceSet
? Возможно, я что-то упускаю, но это звучит как ненужное усложнение.
@dumetrulo Я очень ценю это. Я могу думать об этом совершенно неправильно, но да, я изначально представлял DiceSet
как обертку для List<Dice>
. Но я думаю, что понимаю, что мне действительно нужны Dice : DiceSet
и List<DiceSet>
. Это верно?
В моем дизайне у DiceSet
есть коллекция Dice
, вот и все. Нет наследства. Может быть, набор имеет некоторые методы или свойства для броска всех кубиков или возврата результатов всех бросков, в то время как у Dice
есть методы или свойства для броска самого себя и возврата своего собственного результата.
@Oliver Да, именно так я изначально реализовал свой дизайн. Они оба реализуют интерфейс IRollable
, и когда Roll()
вызывается для DiceSet
, он вызывает Roll()
для каждого Dice
в списке. Я хотел иметь возможность складывать два Dice
вместе, чтобы получить DiceSet
, и чтобы DiceSet
можно было неявно преобразовать в Dice
, но только если он имеет ровно один. Я хочу иметь возможность создать новый кубик с «1d6» и добавить «1d6+1», чтобы получить «2d6+1», или добавить «1d4-1», чтобы получить «1d6 + 1d4 — 1».
Будет ли словарь более подходящим, чем список? Я мог бы ввести его по количеству сторон на кубике и получить кортеж, содержащий количество и модификатор. Затем мой метод Roll()
будет получать случайное целое число для каждого ключа, для каждой величины. Однако мне пришлось бы полностью пересмотреть мой класс Dice
, чтобы это сработало.
Все еще не совсем понимаю, почему вам нужно неявно передать его в Dice ... почему бы не позволить обоим реализовать указанный интерфейс и не только иметь Roll
, но и любой метод/свойство, которое должно поддерживаться обоими ... Я не понимаю вашего формулы есть, но похоже, что ваш IRollable
может также иметь некоторое свойство геттера, и набор может обрабатывать как случаи с одним, так и с несколькими кубиками
@derHugo Да, я не знаю, о чем я думал изначально. Сейчас занимаюсь рефакторингом. В моем проекте гораздо больше интерфейсов и базовых классов, чем я описал в своем вопросе, поэтому их изменение занимает немного времени. Скоро выложу код.
@Oliver Поскольку вы ответили на мой вопрос в своем первом комментарии, вы можете опубликовать его. Спасибо. Если у вас есть какие-либо другие предложения или указатели, я внимательно слушаю, но мне, вероятно, придется опубликовать другой вопрос, если мне понадобится дополнительная помощь.
В зависимости от функциональности, которую должен предоставлять DiceSet, вы можете использовать Составной шаблон C#, чтобы клиенты, работающие с классом DiceSet, не знали, сколько Dice находится в DiceSet.
Не могли бы вы уточнить, как я могу применить это к своему проекту? Я прочитал ссылку, которую вы предоставили, и я уже делаю что-то подобное, но я точно не знаю, как добиться того, что вы предлагаете.
Ваш ответ может быть улучшен с помощью дополнительной вспомогательной информации. Пожалуйста, отредактируйте , чтобы добавить дополнительные сведения, такие как цитаты или документация, чтобы другие могли подтвердить правильность вашего ответа. Вы можете найти больше информации о том, как писать хорошие ответы в справочном центре.
В C# метод может возвращать только один тип. Также невозможно объявить два метода с одинаковым именем, одинаковыми параметрами и разными типами возвращаемого значения.
В зависимости от сценария иногда необходимо либо возвращать разные виды одного и того же, чего можно добиться, возвращая интерфейс и используя разные его реализации, либо возвращать разное количество элементов одного типа (или интерфейса) .
Второй случай соответствует вашей проблеме. Здесь вы должны вернуть тип коллекции. Затем вызывающий объект может проверить, содержит ли коллекция ноль, один или много элементов, и действовать соответствующим образом. Тип возвращаемой коллекции зависит от нескольких факторов. В зависимости от вашего варианта использования вы должны рассмотреть IEnumerable<T>
, IReadOnlyCollection<T>
, IReadOnlyList<T>
, IList<T>
, ISet<T>
или даже конкретный класс, такой как List<T>
или HashSet<T>
.
Обратите внимание, что особенно интерфейс IEnumerable<T>
может оцениваться лениво. Это может привести к некоторым улучшениям производительности при разумном использовании или к ухудшению производительности, непреднамеренному поведению или странным эффектам при неправильном использовании.
Итак, в вашем конкретном случае у вас уже есть оболочка для набора произвольного числа Dice
с именем DiceSet
. Этот набор должен иметь коллекцию Dice
и работать с ней. В этом случае не имеет значения, содержит ли набор один или несколько кубиков, пользователь набора всегда выполняет методы класса набора или, возможно, выполняет итерацию по всем содержащимся кубикам, вызывает для них метод и агрегирует результат, где он также не имеет значения, содержит ли коллекция один или несколько элементов.
Если вам нужно вернуть изменяемое количество элементов, вы всегда должны вернуть какое-то перечисление (либо IEnumerable, либо Collection, List и т. д.). Если потребитель затем перебирает заданную последовательность, она может содержать ноль, один или несколько элементов.