Пустой массив в .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;
}