Массив только для чтения в .NET - PullRequest
3 голосов
/ 05 июня 2009

Массивы - это быстрый способ перебора неупорядоченного набора элементов, и для них часто приятно быть доступными только для чтения. Хотя предоставление массивов с ключевым словом readonly бесполезно, поскольку содержимое массива все еще можно изменить, оболочка ReadOnlyCollection решает эту проблему. Проблема в том, что он в 4 раза медленнее, чем обычный массив в тестах, которые я сделал. (Я знаю, что возвращение копии массива приведет к снижению производительности только один раз, но в идеале я бы не хотел тратить на это время процессора).

Я заметил, что могу получить преимущество проверки работоспособности только для чтения, сохраняя при этом производительность простого массива с таким классом, как этот:

class ReadOnlyArray<T>
{
    private readonly T[] array;

    public ReadOnlyArray(T[] a_array)
    {
        array = a_array;
    }

    // read-only because no `set'
    public T this[int i]
    { get { return array[i]; } }

    public int Length
    { get { return array.Length; } }
}

Проблема в том, что я теряю удобство синтаксиса foreach (). То есть Я должен перебрать его с помощью цикла for (;;), чтобы сохранить производительность. - Я писал код C, где каждый цикл был для (;;). Может быть, я испортился. - Если я реализую IEnumerable , то получаю ту же производительность, что и ReadOnlyCollection , и этот класс бесполезен.

Есть идеи, как добиться идеального сочетания всех трех целей: проверка работоспособности только для чтения, отсутствие потери производительности и удобный синтаксис foreach ()?

Ответы [ 2 ]

4 голосов
/ 05 июня 2009

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

РЕДАКТИРОВАТЬ: я настроил и лучше объяснил приведенный ниже код и упомянул несколько вариантов.

using System.Collections;
using System.Collections.Generic;

/*
  You can leave off the interface, or change to IEnumerable.  See below.
*/
class ReadOnlyArray<T> : IEnumerable<T>
{
    private readonly T[] array;

    public ReadOnlyArray(T[] a_array)
    {
        array = a_array;
    }

    // read-only because no `set'
    public T this[int i]
    { get { return array[i]; } }

    public int Length
    { get { return array.Length; } }

    /* 
       You can comment this method out if you don't implement IEnumerable<T>.
       Casting array.GetEnumerator to IEnumerator<T> will not work.
    */
    public IEnumerator<T> GetEnumerator()
    {
        foreach(T el in array)
        {
            yield return el;
        }
    }

    /* 
       If you don't implement any interface, change this to:
       public IEnumerator GetEnumerator()

       Or you can implement only IEnumerable (rather than IEnerable<T>)
       and keep "IEnumerator IEnumerable.GetEnumerator()"
    */
    IEnumerator IEnumerable.GetEnumerator()
    {
        return array.GetEnumerator();
    }
}
0 голосов
/ 05 июня 2009

Массивы - это быстрый способ перебора неупорядоченного набора элементов,

Если это все, что вам нужно сделать, просто верните массив как IEnumerable. Не реализуйте IEnumerable самостоятельно: массив уже это делает.

Это должно соответствовать всем трем вашим целям.

public class SomeClass
{ 
    private T[] myArray; // T could be any type

    public IEnumerable<T> MyReadOnlyArray { get { return myArray; } }
}
...