.NET中的空数组是否使用任何空间?
我有一些代码,我正在返回一个对象数组。
这是一个简化的例子:
string[] GetTheStuff() { List s = null; if( somePredicate() ) { s = new List(); // imagine we load some data or something } return (s == null) ? new string[0] : s.ToArray(); }
问题是, new string[0]
有多贵?
我应该只返回null并使调用者接受null作为指示“未找到任何内容”的有效方式吗?
注意:这是在循环中调用的,它会运行数百次,所以这是我认为这种优化实际上并不“过早”的少数情况之一。
PS:即使它还为时过早,我仍然想知道它是如何工作的:-)
更新:
最初,当我问它是否使用了任何空间时,我从’C / C ++’的角度思考问题,有点像在C中编写char a[5];
将在堆栈上分配5个字节的空间,并且char b[0];
将分配0个字节。
我意识到这不适合.NET世界,但我很好奇,如果这是编译器或CLR将检测和优化的东西,因为一个大小为零的不可resize的数组真的不应该(至于我可以看到?)需要任何存储空间。
即使它被称为“数百和数百”次,我也会说这是一个不成熟的优化。 如果结果作为空数组更清晰,请使用它。
现在得到实际答案:是的,一个空数组需要一些内存。 它具有正常的对象开销(我相信x86上为8个字节),计数为4个字节。 我不知道除此之外还有什么,但它并不是完全免费的。 (它虽然非常便宜……)
幸运的是,您可以在不牺牲API本身的情况下进行优化:拥有空数组的“常量”。 如果你允许的话,我已经做了另一个小改动,使代码更清晰……
private static readonly string[] EmptyStringArray = new string[0]; string[] GetTheStuff() { if( somePredicate() ) { List s = new List (); // imagine we load some data or something return s.ToArray(); } else { return EmptyStringArray; } }
如果你经常发现自己需要这个,你甚至可以创建一个带有静态成员的generics类来返回一个正确类型的空数组。 .NETgenerics的工作方式使这一点变得微不足道:
public static class Arrays { public static readonly Empty = new T[0]; }
(当然,你可以将它包装在一个属性中。)
然后只需使用:Arrays
编辑:我刚刚记得Eric Lippert关于数组的post 。 您确定数组是最合适的类型吗?
即将推出的.NET 4.6版(2015年晚些时候)包含一个返回长度为零的string[]
的静态方法 :
Array.Empty()
我想如果多次调用它会返回相同的实例。
声明的数组总是必须包含以下信息:
- 等级(维数)
- 要包含的类型
- 每个维度的长度
这很可能是微不足道的,但是对于更大数量的尺寸和更长的长度, 它将对环路产生性能影响。
至于返回类型,我同意应该返回一个空数组而不是null。
更多信息: .NET中的数组类型
是的,正如其他人所说的那样,空数组占用了对象头和长度字段的几个字节。
但是如果你担心性能问题,你就会关注这种方法中错误的执行分支。 我更关心填充列表上的ToArray调用,这将导致内存分配等于其内部大小和列表内容的内存副本。
如果你真的想提高性能,那么(如果可能的话)通过使返回类型之一直接返回列表: List
取决于你需要什么设施它(注意在一般情况下不太具体是更好的)。
我猜想一个空数组只使用分配对象指针本身所需的空间。
从内存中,API指南说你应该总是从返回数组的方法返回一个空数组,而不是返回null,所以我不管你的代码是什么样的。 这样调用者知道他保证得到一个数组(即使是空数组),并且不需要在每次调用时检查null。
编辑:关于返回空数组的链接:
http://wesnerm.blogs.com/net_undocumented/2004/02/empty_arrays.html
其他人很好地回答了你的问题。 所以只是一个简单的要点……
我会避免返回一个数组(除非你不能)。 坚持IEnumerable然后你可以使用LINQ API中的Enumerable.Empty
。 显然,Microsoft已经为您优化了这种情况。
IEnumerable GetTheStuff() { List s = null; if (somePredicate()) { var stuff = new List (); // load data return stuff; } return Enumerable.Empty (); }
这不是您问题的直接答案。
了解为什么数组被认为有些危害 。 我建议你在这种情况下返回一个IList
IList GetTheStuff() { List s = new List (); if( somePredicate() ) { // imagine we load some data or something } return s; }
通过这种方式,调用者不必关心空返回值。
编辑 :如果返回的列表不可编辑,您可以将List包装在ReadOnlyCollection中 。 只需将最后一行更改为。 我也会考虑这个最佳实践。
return new ReadOnlyCollection(s);
如果我理解正确,将为字符串数组分配少量内存。 您的代码本质上需要创建一个通用列表,所以为什么不返回呢?
[编辑]删除了返回空值的代码版本。 在这种情况下建议反对空回报值的其他答案似乎是更好的建议[/ EDIT]
List GetTheStuff() { List s = new List
我知道这是一个老问题,但这是一个基本问题,我需要一个详细的答案。
所以我探索了这个并得到了结果:
在.Net中创建数组时(对于此示例,我使用int[]
),在为数据分配任何内存之前需要6个字节 。
考虑这段代码[在32位应用程序中!]:
int[] myArray = new int[0]; int[] myArray2 = new int[1]; char[] myArray3 = new char[0];
看看记忆:
myArray: a8 1a 8f 70 00 00 00 00 00 00 00 00 myArray2: a8 1a 8f 70 01 00 00 00 00 00 00 00 00 00 00 00 myArray3: 50 06 8f 70 00 00 00 00 00 00 00 00
让我们解释一下记忆:
- 看起来前2个字节是某种元数据,因为你可以看到它在
int[]
和char[]
之间发生变化(a8 1a 8f 70
vs50 06 8f 70
) - 然后它以整数变量(little endian)保存数组的大小。 因此
myArray
为01 00 00 00
,myArray2
为01 00 00 00
- 现在这是我们宝贵的数据 [我用立即窗口测试]
- 之后我们看到一个常数(
00 00 00 00
)。 我不知道它的含义是什么。
现在我对零长度数组感觉好多了,我知道它是如何工作的 =]