Лучший гибридный подход к многомерному массиву со строгой типизированной индексацией - PullRequest
4 голосов
/ 22 февраля 2009

У меня есть то, что составляет многомерный массив.

int[][][] MyValues;

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

Я бы предпочел иметь способ строго набирать индексы.

Например:

int CarNumber = MyValues[Racetrack.Daytona][Race.Daytona500][Driver.JGordon];

Это, в силу того, что это enum-like, предотвратит выбрасывание любых исключений вне пределов, плюс оно придает всем индексам приятный для восприятия человеком смысл.

Я реализовал это, используя словарный подход, но это выглядит довольно жестко:

Dictionary<Racetrack,Dictionary<Race,<Dictionary<Driver,int>>> =
    new Dictionary<Racetrack,Dictionary<Race,<Dictionary<Driver,int>>>();

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

Я ищу несколько альтернативных методов для представления того, что по сути является многомерным массивом, с использованием удобочитаемых человеком индексаторов, при сохранении безопасности типов (например, нельзя случайно использовать драйвер для гонки, просто использование consts не очень хороший подход).

Есть предложения?

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

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

Ответы [ 5 ]

5 голосов
/ 22 февраля 2009

Мне кажется, что вы хотите использовать indexers вместо массива. Предполагая следующие перечисления (на основе формулы 1!):

public enum Track
{
    Spielberg,
    Adelaide,
    Casablanca,
    Liverpool,
    Melbourne,
    Berlin,
    Sakhir,
}

public enum Constructor
{
    BMW,
    Ferrari,
    McLaren,
    Toyota,
    Williams
}

public enum Driver
{
    Hamilton,
    Kovalainen,
    Raikkonen,
    Nakajima,
    Glock
}

Основная структура выглядит следующим образом:

public class Race
{
    int Year { get; set; }
    Track Track { get; set; }
    Driver[] Placings { get; set; }
    public int this[Driver driver] { } // placing by driver
}

public class Results
{
    YearResults this[int index] { }
    DriverResults this[Driver index] { }
    TrackResults this[Track index] { }
    ConstructorResults this[Constructor index] { }
}

public class YearResults
{
    YearDriverResults this[Driver index] { }
}

Это, конечно, частичная реализация, но вы можете сделать довольно интересные вещи с индексаторами таким образом. Как вы можете получить доступ к вашей информации с любой комбинацией значений в любом порядке (при условии, что вы настроили все промежуточные классы).

Он более многословный, чем многомерный массив или словарь с кортежем, но я думаю, он даст вам гораздо более элегантный код.

2 голосов
/ 22 февраля 2009

Как насчет использования тройного <Racetrack,Race,Driver> в качестве ключа (определите свой собственный класс) в Dictionary?

Если вам действительно нужно использовать массив, я не думаю, что вы можете сделать это лучше, чем помещать его в собственный класс, который разрешает доступ только с использованием Racetrack, Race, Driver перечислений.

0 голосов
/ 22 февраля 2009

Я не думаю, что словарный подход плох, но он не элегантен. Если бы вы создали псевдоним для своего словаря словаря, вещи выглядели бы лучше:

using RaceSetup = Dictionary<Racetrack,Dictionary<Race,<Dictionary<Driver,int>>>;

Или вы можете создать класс, производный от словаря:

class RaceSetup : Dictionary<Racetrack,Dictionary<Race,<Dictionary<Driver,int>>>
{}
0 голосов
/ 22 февраля 2009

Достаточно ли малы перечисления со значениями 0 ... n? Если это так, вы можете использовать многомерный массив, но выставить индексатор. Обратите внимание, что в приведенном ниже коде используется прямоугольный массив, а не зубчатый массив, но его можно довольно легко адаптировать.

// In a static class somewhere. Just a convenience method to check
// whether a value is defined or not. See comment in indexer.
public static void CheckDefined<T>(this T value, String name)
    where T : struct
{
    if (!Enum.IsDefined(typeof(T), value))
    {
        throw new ArgumentOutOfRangeException(name);
    }
}


// Somewhere else...
private static int GetLength<T>() where T : struct
{
    return Enum.GetValues(typeof(T)).Length;
}

private int[,,] array = new int[GetLength<Racetrack>(), 
                                GetLength<Race>(),
                                GetLength<Driver>()];

public int this Car[Racetrack racetrack, Race race, Driver driver]
{
  get
  {
    // If you don't care about just getting an
    // IndexOutOfRangeException, you could skip these three lines.
    racetrack.CheckDefined("racetrack");
    race.CheckDefined("race");
    driver.CheckDefined("driver");
    return array[(int) racetrack, (int) race, (int) driver);
  }
}
0 голосов
/ 22 февраля 2009

Очевидный вопрос .. List<T> не подойдет для вас?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...