Пустой массив в .NET использует любое пространство?

У меня есть код, в котором я возвращаю массив объектов.

Здесь приведен упрощенный пример:

string[] GetTheStuff() {
    List<string> s = null;
    if( somePredicate() ) {
        s = new List<string>(); // imagine we load some data or something
    }
    return (s == null) ? 
        new string[0] :
        s.ToArray();
}

Вопрос в том, насколько дорогим является new string[0]?
Должен ли я просто вернуть значение null и заставить вызывающего пользователя принять значение null как действительный способ указания "ничего не найдено"?

NB: Это вызывается в цикле, который запускается сотни и сотни раз, поэтому это один из немногих случаев, когда я думаю, что такое оптимизация не является преждевременным.

PS: И даже если это было преждевременно, мне все равно хотелось бы знать, как это работает: -)

Обновление:

Изначально, когда я спросил, использует ли оно какое-либо пространство, я думал о вещах с точки зрения "C/С++", вроде как в C, пишу char a[5]; выделяет 5 байтов пространства в стеке, и char b[0]; будет выделять 0 байт.

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

Ответ 1

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

Теперь для фактического ответа: да, пустой массив занимает некоторую память. Он имеет нормальные служебные данные объекта (по моему мнению, 8 байтов на x86) и 4 байта для счета. Я не знаю, есть ли что-то помимо этого, но это не совсем бесплатно. (Это невероятно дешево, хотя...)

К счастью, можно сделать оптимизацию без ущерба для самого API: иметь "константу" пустого массива. Я сделал еще одно небольшое изменение, чтобы сделать код более четким, если вы позволите...

private static readonly string[] EmptyStringArray = new string[0];

string[] GetTheStuff() {
    if( somePredicate() ) {
        List<string> s = new List<string>(); 
        // imagine we load some data or something
        return s.ToArray();
    } else {
        return EmptyStringArray;
    }
}

Если вы так часто нуждаетесь в этом, вы даже можете создать общий класс со статическим членом для возврата пустого массива нужного типа. Способ работы generics.NET делает это тривиальным:

public static class Arrays<T> {
    public static readonly Empty = new T[0];
}

(Конечно, вы можете обернуть его в свойство.)

Затем просто используйте: Arrays <string> .Empty;

EDIT: Я только что вспомнил сообщение Эрика Липперта на массивах. Вы уверены, что массив является наиболее подходящим типом для возврата?

Ответ 2

Предстоящая версия 4.6.NET(позже в 2015 году) содержит статический метод, возвращающий длину-ноль string[]:

Array.Empty<string>()

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

Ответ 3

Объявленные массивы всегда должны содержать следующую информацию:

  • Ранг (количество измерений)
  • Тип, который будет содержаться
  • Длина каждого измерения

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

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

Дополнительная информация здесь: Типы массивов в .NET

Ответ 4

Да, как говорили другие, пустой массив занимает несколько байтов для заголовка объекта и поля длины.

Но если вы беспокоитесь о производительности, вы фокусируетесь на неправильной ветки исполнения в этом методе. Меня гораздо больше беспокоит вызов ToArray в заполненном списке, который приведет к распределению памяти, равному его внутреннему размеру и копии памяти содержимого списка.

Если вы действительно хотите повысить производительность, то (если возможно) верните список напрямую, указав тип возврата один из: List<T>, IList<T>, ICollection<T>, IEnumerable<T> в зависимости от того, какие средства вам нужны (обратите внимание, что в общем случае менее конкретный).

Ответ 5

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

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

Изменить: ссылка о возвращении пустых массивов:

http://wesnerm.blogs.com/net_undocumented/2004/02/empty_arrays.html

Ответ 6

Другие ответили на ваш вопрос красиво. Итак, просто пункт, чтобы сделать...

Я бы не возвращал массив (если вы не можете). Stick с IEnumerable, а затем вы можете использовать Enumerable.Empty<T>() из API LINQ. Очевидно, Microsoft оптимизировала этот сценарий для вас.

IEnumerable<string> GetTheStuff()
{
    List<string> s = null;
    if (somePredicate())
    {
        var stuff = new List<string>();
        // load data
        return stuff;
    }

    return Enumerable.Empty<string>();
}

Ответ 7

Это не прямой ответ на ваш вопрос.

Читайте, почему массивы считаются несколько вредными. Я бы предложил вам вернуть IList <string> в этом случае и немного измените структуру кода:

IList<string> GetTheStuff() {
    List<string> s = new List<string>();
    if( somePredicate() ) {
        // imagine we load some data or something
    }
    return s;
}

Таким образом, вызывающему абоненту не нужно заботиться о пустых возвращаемых значениях.


EDIT. Если возвращаемый список не может быть доступен для редактирования, вы можете обернуть список внутри ReadOnlyCollection. Просто измените последнюю строку. Я также рассмотрю эту лучшую практику.

    return new ReadOnlyCollection(s);

Ответ 8

Если я правильно понимаю, для массивов строк будет выделен небольшой объем памяти. Вы, по сути, требуете создания универсального списка, так почему бы просто не вернуть его?

[EDIT] Удалена версия кода, которая вернула нулевое значение. Другие ответы, предупреждающие об отрицательных значениях возврата в этом случае, кажутся лучшим советом [/EDIT]

List<string> GetTheStuff()
{
   List<string> s = new List<string();
   if (somePredicarte())
   {
      // more code
   }
   return s;
}