.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 .Empty;

编辑:我刚刚记得Eric Lippert关于数组的post 。 您确定数组是最合适的类型吗?

即将推出的.NET 4.6版(2015年晚些时候)包含一个返回长度为零的string[] 的静态方法 :

 Array.Empty() 

我想如果多次调用它会返回相同的实例。

声明的数组总是必须包含以下信息:

  • 等级(维数)
  • 要包含的类型
  • 每个维度的长度

这很可能是微不足道的,但是对于更大数量的尺寸和更长的长度, 它将对环路产生性能影响。

至于返回类型,我同意应该返回一个空数组而不是null。

更多信息: .NET中的数组类型

是的,正如其他人所说的那样,空数组占用了对象头和长度字段的几个字节。

但是如果你担心性能问题,你就会关注这种方法中错误的执行分支。 我更关心填充列表上的ToArray调用,这将导致内存分配等于其内部大小和列表内容的内存副本。

如果你真的想提高性能,那么(如果可能的话)通过使返回类型之一直接返回列表: List, IList, ICollection, IEnumerable取决于你需要什么设施它(注意在一般情况下不太具体是更好的)。

我猜想一个空数组只使用分配对象指针本身所需的空间。

从内存中,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 vs 50 06 8f 70
  • 然后它以整数变量(little endian)保存数组的大小。 因此myArray01 00 00 00myArray201 00 00 00
  • 现在这是我们宝贵的数据 [我用立即窗口测试]
  • 之后我们看到一个常数( 00 00 00 00 )。 我不知道它的含义是什么。

现在我对零长度数组感觉好多了,我知道它是如何工作的 =]