Итерации по элементам в массиве с неизвестными параметрами в C#

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

private void Form1_Load(object sender, EventArgs e)
{
    string[, ,] values =
    {
        {
            { "(0, 0, 0)", "(0, 0, 1)", "(0, 0, 2)", "(0, 0, 3)", },
            { "(0, 1, 0)", "(0, 1, 1)", "(0, 1, 2)", "(0, 1, 3)", },
        },
        {
            { "(1, 1, 0)", "(1, 1, 1)", "(1, 1, 2)", "(1, 0, 3)", },
            { "(1, 1, 0)", "(1, 1, 1)", "(1, 1, 2)", "(1, 1, 3)", },
        },
        {
            { "(2, 1, 0)", "(2, 1, 1)", "(2, 1, 2)", "(2, 0, 3)", },
            { "(2, 1, 0)", "(2, 1, 1)", "(2, 1, 2)", "(2, 1, 3)", },
        },
    };
    
    // Отображение значений.
    txtValues.Text = GetArrayValueString(values);
    txtValues.Select(0, 0);
}

Этот код создает трехмерный массив целых чисел. Затем он вызывает следующий метод GetArrayValueString, чтобы получить строковое представление значений массива и отобразить результат.

// Возвращает строку, показывающую значения массива.
private string GetArrayValueString(Array array)
{
    string result = "";

    // Получить ранг массива (количество измерений).
    int rank = array.GetType().GetArrayRank();

    // Получаем верхние границы размеров массива.
    int[] counts = new int[rank];
    for (int i = 0; i < rank; i++)
    {
        counts[i] = array.GetUpperBound(i) + 1;
    }

    // Создаем массив для отображения текущих индексов.
    int[] indices = new int[rank];

    // Рекурсивно перечислим элементы в первом измерении.
    result = ListItemsForDimension(array, counts, indices, 0);

    // Замените последнее, с помощью.;
    int last_comma_pos = result.LastIndexOf(",");
    result = result.Substring(0, last_comma_pos) + ";\r\n";

    return result;
}

Этот код получает объект типа массива, а затем использует метод GetArrayRank этого объекта, чтобы определить, сколько измерений имеет массив. Массив этого примера имеет три измерения, хотя метод работает для массивов с любым количеством измерений.

Далее код делает массив counts для хранения количества элементов в каждом измерении. Он перебирает размеры, использует метод GetUpperBound, чтобы получить подсчет каждого измерения, и сохраняет счет в массиве counts.

Поскольку вы не знаете количество измерений при написании кода, вы не можете использовать обычную индексацию массива для подключения чисел к программе для получения значений. Например, вы не можете сказать массив [1, 2, 3], потому что вы не знаете, что массив имеет 3 измерения.

Чтобы получить значения из массива, вам нужно использовать метод массива GetValue, передающий ему массив индексов. Чтобы получить значение в позиции [1, 2, 3], вы должны передать в метод массив, содержащий индексы 1, 2 и 3.

Чтобы подготовиться к этому, метод GetArrayValueString создает массив indices. По мере того как программа рекурсирует, она заполняет записи в этом массиве. Когда он находится в конце серии рекурсивных вызовов, этот массив содержит индекс для каждого измерения в массиве, поэтому программа может передать его методу GetValue.

Создав массив indices, метод GetArrayValueString вызывает ListItemsForDimension, передавая ему массив значений, counts массив, содержащий счетчик для каждого измерения, текущий пустой массив indices и 0, чтобы указать, что ListItemsForDimension должен работать с первым измерением массива.

Результат, возвращаемый ListItemsForDimension, заканчивается запятой и новой строкой. Чтобы результат выглядел как действительный инициализатор массива C#, код заменяет конечную запятую точкой с запятой.

В следующем коде показан рекурсивный метод ListItemsForDimension.

// Рекурсивно перечислим элементы для указанного измерения.
private string ListItemsForDimension(Array array,
    int[] counts, int[] indices, int dimension)
{
    string indent = new string(' ', dimension * 4);

    // Посмотрим, является ли это самым внутренним измерением.
    if (dimension == counts.Length - 1)
    {
        string result = indent + "{ ";

        // Перебираем индексы для этого измерения.
        for (int i = 0; i < counts[dimension]; i++)
        {
            // Устанавливаем индекс для этого элемента.
            indices[dimension] = i;

            // Получить значение элемента.
            result += "\"" + array.GetValue(indices).ToString() + "\", ";
        }

        result += "},\r\n";
        return result;
    }
    else
    {
        string result = indent + "{\r\n";
        
        // Перебираем индексы для этого измерения.
        for (int i = 0; i < counts[dimension]; i++)
        {
            // Устанавливаем индекс для этого элемента.
            indices[dimension] = i;

            // Рекурсивно перечислим подпозиции.
            result += ListItemsForDimension(array,
                counts, indices, dimension + 1);
        }

        result += indent + "},\r\n";
        return result;
    }
}

Метод ListItemsForDimension возвращает строку, показывающую элементы в массиве, где индексы перед указанным размером хранятся в массиве индексов. Например, предположим, что массив имеет 4 измерения, размерность = 2 и индексы = [2, 1, 3, 4]. Поскольку размерность = 2, важны только первые две записи в массиве индексов. В этом случае ListItemsForDimension должно возвращать представление массива значений [2, 1, x, y], где x и y меняются по всем возможным значениям для их размеров.

Для этого ListItemsForDimension сначала создает строку, которая может использоваться для отступов от ее результатов. Затем он проверяет, указывает ли измерение конечный размер массива.

Если это последнее измерение, код перебирает допустимые значения индекса для окончательного измерения. Для каждого значения индекса он устанавливает последнюю запись в массиве index. В этот момент заполняется каждая запись в индексах, поэтому эти значения представляют определенную позицию в массиве. Код передает массив indices в метод GetValue массива, чтобы получить значение в этой позиции и добавляет его в результирующую строку. После того, как он зациклился на всех значениях индекса для этого измерения, код добавляет замыкающую скобку в строку результатов и возвращает ее.

Если это не последнее измерение, метод ListItemsForDimension добавляет открытую скобку в строку результатов и запускает новую строку. Затем он перебирает значения индекса, допустимые для этого измерения. Для каждого индекса он сохраняет индекс в следующей позиции в массиве indices, а затем вызывает себя рекурсивно, чтобы получить представление значений, где задано это измерение. Он добавляет возвращаемый результат в свою строку результатов и продолжается для других допустимых значений индекса. После добавления представлений о допустимых значениях индекса метод ListItemsForDimension заканчивает свое собственное представление добавлением закрывающей скобки и возвращает результат.

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

В этом примере показано, как вы можете перебирать элементы в массиве с неизвестными измерениями. Легче использовать цикл foreach для перебора элементов. Например, следующий код отображает все значения в массиве в окне консоли.

foreach (string value in values) Console.WriteLine(value);

К сожалению, код внутри цикла не знает, какие размеры соответствуют каждому значению. Например, он не знал бы номер строки каждого элемента.

Источник: http://csharphelper.com/blog/2017/08/iterate-over-items-in-an-array-with-unknown-dimensions-in-c/

1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (Пока оценок нет)
Adblock
detector