我有一些代码,我正在返回一个对象数组。
这是一个简化的例子:
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作为指示“未找到任何内容”的有效方式吗?
注意:这是在循环中调用的,它会运行数百次,所以这是我认为这种优化实际上并不“过早”的少数情况之一。
PS:即使它还为时过早,我仍然想知道它是如何工作的: - )最初,当我问它是否使用了任何空间时,我从'C / C ++'的角度思考问题,有点像在C中,写char a[5];
将分配5个字节的空间堆栈,char b[0];
将分配0个字节。
我意识到这不适合.NET世界,但我很好奇,如果这是编译器或CLR会检测并优化的东西,因为大小为零的不可调整大小的数组真的不应该(据我所见?)需要任何存储空间。
答案 0 :(得分:56)
即使它被称为“数百和数百”次,我也说这是一个不成熟的优化。如果结果作为空数组更清晰,请使用它。
现在得到实际答案:是的,空数组需要一些内存。它具有正常的对象开销(我相信x86上为8个字节),计数为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;
}
}
如果你经常发现自己需要这个,你甚至可以创建一个带有静态成员的泛型类来返回一个正确类型的空数组。 .NET泛型的工作方式使这一点变得微不足道:
public static class Arrays<T> {
public static readonly Empty = new T[0];
}
(当然,你可以将它包装在一个属性中。)
然后只使用:Arrays&lt; string&gt; .Empty;
编辑:我刚才记得Eric Lippert's post on arrays。您确定数组是最适合返回的类型吗?答案 1 :(得分:9)
答案 2 :(得分:5)
声明的数组必须始终包含以下信息:
这很可能是微不足道的,但是对于更大数量的维度和更长的长度,将对循环产生性能影响。
至于返回类型,我同意应该返回一个空数组而不是null。
此处提供更多信息:Array Types in .NET
答案 3 :(得分:4)
是的,正如其他人所说,空数组占用了对象标题和长度字段的几个字节。
但是如果你担心性能问题,你就会关注这种方法中错误的执行分支。我会更加关注填充列表中的 ToArray 调用,这将导致内存分配等于其内部大小以及列表内容的内存副本。
如果你真的想要提高性能,那么(如果可能的话)通过将返回类型设置为List<T>, IList<T>, ICollection<T>, IEnumerable<T>
之一来直接返回列表,具体取决于你需要它的设施(请注意,一般情况下更不具体情况)。
答案 4 :(得分:3)
我猜想空数组只使用分配对象指针本身所需的空间。
从内存中,API指南说你应该总是从返回数组而不是返回null的方法返回一个空数组,所以我会保持你的代码不管你的方式。这样调用者知道他保证得到一个数组(即使是空数组),并且不需要在每次调用时检查null。
编辑:关于返回空数组的链接:
http://wesnerm.blogs.com/net_undocumented/2004/02/empty_arrays.html
答案 5 :(得分:3)
其他人很好地回答了你的问题。所以只是一个简单的要点......
我会避免返回一个数组(除非你不能)。坚持IEnumerable,然后您可以使用LINQ API中的Enumerable.Empty<T>()
。显然,微软已经为您优化了这种情况。
IEnumerable<string> GetTheStuff()
{
List<string> s = null;
if (somePredicate())
{
var stuff = new List<string>();
// load data
return stuff;
}
return Enumerable.Empty<string>();
}
答案 6 :(得分:2)
这不是您问题的直接答案。
阅读arrays are considered somewhat harmful的原因。我建议你返回一个IList&lt; string&gt;在这种情况下,重新编写代码:
IList<string> GetTheStuff() {
List<string> s = new List<string>();
if( somePredicate() ) {
// imagine we load some data or something
}
return s;
}
通过这种方式,调用者不必关心空返回值。
编辑:如果返回的列表不可编辑,您可以将List包装在ReadOnlyCollection内。只需将最后一行更改为。我也会考虑这个最佳实践。
return new ReadOnlyCollection(s);
答案 7 :(得分:0)
如果我理解正确,将为字符串数组分配少量内存。你的代码本质上要求创建一个通用列表,所以为什么不返回呢?
[EDIT]删除了返回null值的代码版本。在这种情况下建议反对空回报值的其他答案似乎是更好的建议[/ EDIT]
List<string> GetTheStuff()
{
List<string> s = new List<string();
if (somePredicarte())
{
// more code
}
return s;
}
答案 8 :(得分:0)
我知道这是一个老问题,但这是一个基本问题,我需要详细的答案。
所以我对此进行了探索并得到了结果:
在.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
int[]
和char[]
之间变化( a8 1a 8f 70
vs 50 06 8f 70
) 00 00 00 00
是myArray
,01 00 00 00
是myArray2
00 00 00 00
)。我不知道这是什么意思。现在,我对零长度数组感觉好多了,我知道它的工作原理 =]