我有一些代码要在其中返回对象数组。
这是一个简化的示例:
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个字节的空间,而将分配0个字节。
我意识到这不是.NET世界的理想选择,但我很好奇这是否是编译器或CLR可以检测并优化的东西,因为大小为零的不可调整大小的数组实际上不应该(到目前为止)我可以看到吗?)需要任何存储空间。
即使它被称为"数百次",也要说是过早的优化。如果结果更清晰地显示为空数组,请使用该数组。
现在是实际答案:是的,一个空数组会占用一些内存。它具有正常的对象开销(我相信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;
}
}
如果发现自己经常需要此功能,则甚至可以创建带有静态成员的泛型类以返回正确类型的空数组。 .NET泛型的工作方式很简单:
public static class Arrays< T > {
public static readonly Empty = new T[0];
}
(当然,您可以将其包装在属性中。)
然后只需使用:Arrays .Empty;
编辑:我只是想起了Eric Lippert在数组上的帖子。您确定数组是最适合返回的类型吗?
h香港专业教育学院实际上已经使用了数十次返回恒定技巧。我没想到就傻了:-)
好招类似于String.Empty :)
当心静态只读数组,因为只有该数组是只读的。内容仍然可变。因此,有人可以将您返回的空数组粘贴在其中,从而使您返回非空数组。
嗯,您的编辑内容涵盖了Eric Lipperts的帖子(恰恰是在我脑海中浮现的帖子)。 :)
ffpf:有人会把东西粘在一个空数组中吗?是的,只有数组引用是只读的-但是数组仍然是固定大小0。
我认为ffpf不知道在Java和.NET中数组是固定大小的,不能放大或缩小。只能更改其内容,但是此内容非常空白。
@JeffYates:如何将某物粘贴到一个空(即零大小)数组中?
当时,我更多地考虑将数组返回作为一种不好的做法,并且我没有针对调用Array.Resize尝试此代码。我仍然没有,尽管我确定它会抱怨,因为声明是只读的。
另请参阅有关新Array.Empty()方法的答案,我猜这将使不必编写Arrays.Empty属性。
即将发布的.NET 4.6版本(2015年后期)包含一个返回长度为零的string[]的静态方法:
Array.Empty()
我想如果多次调用它会返回相同的实例。
声明的数组将始终必须包含以下信息:
等级(尺寸数)
要包含的类型
每个尺寸的长度
这很可能是微不足道的,但是对于更大数量的尺寸和更大的长度,它将对循环产生性能影响。
至于返回类型,我同意应该返回一个空数组而不是null。
这里的更多信息:.NET中的数组类型
我希望包含的等级和类型是类型信息本身的一部分(即正常8字节开销的一部分)。我没有检查,请注意。还有下限要考虑,但我认为这将作为另一种类型来处理(在CLR术语中为数组而不是向量)。
是的,正如其他人所说的,空数组占用对象头和长度字段的几个字节。
但是,如果您担心性能,那么您将把重点放在此方法的错误执行分支上。我会更加关注填充列表上的ToArray调用,这将导致与其内部大小相等的内存分配以及列表内容的内存副本进入其中。
如果您确实想提高性能,那么(如果可能)通过将返回类型设为以下之一来直接返回列表:List< T >, IList< T >, ICollection< T >, IEnumerable< T >,具体取决于您需要从中获得什么便利(请注意,一般情况下,不那么具体比较好)。
其他人很好地回答了您的问题。因此,简单说明一下...
我将避免返回数组(除非您不能这样做)。坚持使用IEnumerable,然后可以使用LINQ API中的Enumerable.Empty< T >()。显然,Microsoft已为您优化了此方案。
IEnumerable GetTheStuff()
{
List s = null;
if (somePredicate())
{
var stuff = new List();
// load data
return stuff;
}
return Enumerable.Empty();
}
我猜想一个空数组仅使用分配对象指针本身所需的空间。
API准则从内存中说,您应该始终从返回数组的方法而不是返回null的方法中返回空数组,因此无论如何我都会保留代码。这样,调用方就知道可以保证得到一个数组(甚至是一个空数组),并且不必在每次调用时检查是否为null。
编辑:有关返回空数组的链接:
http://wesnerm.blogs.com/net_undocumented/2004/02/empty_arrays.html
这不是您问题的直接答案。
阅读为什么数组被认为有些有害。我建议您在这种情况下返回一个IList 并重新构建代码:
IList GetTheStuff() {
List s = new List();
if( somePredicate() ) {
// imagine we load some data or something
}
return s;
}
这样,调用方就不必关心空的返回值。
编辑:如果返回的列表不可编辑,则可以将列表包装在ReadOnlyCollection中。只需将最后一行更改为。我也将考虑这种最佳做法。
return new ReadOnlyCollection(s);
不幸的是,列表使呼叫者可以更改项目计数。数组确保长度不变。如果不应该添加或删除它,我建议您这样做。如果只是枚举,最好的选择就是枚举。
不错的文章链接!
我知道这是一个老问题,但这是一个基本问题,我需要详细的答案。
因此,我对此进行了探索并获得了结果:
在.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与50 06 8f 70)之间变化
然后,它将数组的大小保存为整数变量(小端)。因此对于myArray是00 00 00 00,对于myArray2是01 00 00 00
现在是我们的宝贵数据[我使用即时窗口进行了测试]
之后,我们看到一个常量(00 00 00 00)。我不知道这是什么意思。
Now I feel a lot better about zero length array, I know how it works
=]
如果我理解正确,将为字符串数组分配少量内存。您的代码本质上还是需要创建一个通用列表,所以为什么不只返回它呢?
[EDIT]删除了返回空值的代码版本。在这种情况下,建议针对空返回值的其他答案似乎是更好的建议[/ EDIT]
List GetTheStuff()
{
List s = new List
if (somePredicarte())
{
// more code
}
return s;
}
对于琐碎的操作,泛型列表很好,但是对于大量的循环,它们将始终变慢。同样,在后台,通用列表确实是一个数组。使用反射器并检查。 ;)
@乔恩:Dr8k的观点是已经有一个通用列表,因此他的论点仍然有效。
谢谢大卫。你对我的观点是正确的。如果出于性能原因需要返回数组值,则在内部该方法也应使用数组值。
关键是,如果谓词返回true,则只需要构建一个列表。 10的9倍不会,所以如果可以的话,我想避免构造一个List。问题实际上应该是,创建一个空列表而不是一个空数组要花多少钱?