26

オブジェクトの配列を返すコードがいくつかあります。

簡単な例を次に示します。

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 を受け入れさせる必要がありますか?

注意: これは何百回も実行されるループで呼び出されているため、この種の最適化が実際には「時期尚早」ではないと私が考える数少ないケースの 1 つです。

PS: 時期尚早だったとしても、それがどのように機能するか知りたいです :-)

アップデート:

最初にスペースを使用するかどうか尋ねたとき、「C/C++」の観点から物事を考えていました.Cのように、書き込みchar a[5];はスタックに5バイトのスペースchar b[0];を割り当て、0バイトを割り当てます。

これが .NET の世界に適していないことはわかっていますが、これがコンパイラまたは CLR によって検出され、最適化されるものであるかどうかに興味がありました。わかりますか?) 収納スペースが必要です。

4

9 に答える 9

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

編集: Eric Lippert の arrays に関する投稿を思い出しました。配列が返すのに最も適した型であると確信していますか?

于 2008-09-30T06:16:23.193 に答える
5

宣言された配列には、常に次の情報が含まれている必要があります。

  • ランク(次元数)
  • 収容するタイプ
  • 各次元の長さ

これはおそらく些細なことですが、次元数が多く長さが長い場合、ループのパフォーマンスに影響を与えます。

戻り値の型については、null ではなく空の配列を返すことに同意します。

詳細はこちら: .NET の配列型

于 2008-09-30T06:13:23.137 に答える
4

はい、他の人が言ったように、空の配列はオブジェクト ヘッダーと長さフィールドに数バイトを占めます。

ただし、パフォーマンスが心配な場合は、このメソッドで間違った実行ブランチに焦点を合わせています。入力されたリストでのToArray呼び出しについては、内部サイズと同じメモリ割り当てとリストの内容のメモリコピーが発生することにもっと関心があります。

パフォーマンスを本当に改善したい場合は、(可能であれば) 戻り値の型を次のいずれかにして、リストを直接返しList<T>, IList<T>, ICollection<T>, IEnumerable<T>ます。

于 2008-09-30T07:53:45.347 に答える
3

他の人はあなたの質問にうまく答えました。簡単なポイントです...

配列を返すことは避けたいと思います(できない場合を除く)。IEnumerableに固執すると、LINQAPIから使用できEnumerable.Empty<T>()ます。明らかに、Microsoftはこのシナリオを最適化しました。

IEnumerable<string> GetTheStuff()
{
    List<string> s = null;
    if (somePredicate())
    {
        var stuff = new List<string>();
        // load data
        return stuff;
    }

    return Enumerable.Empty<string>();
}
于 2009-02-16T17:54:32.097 に答える
3

空の配列は、オブジェクト ポインター自体を割り当てるために必要なスペースのみを使用すると思います。

メモリから、API ガイドラインでは、null を返すのではなく、配列を返すメソッドから常に空の配列を返す必要があると記載されているため、コードはそのままにしておきます。そうすれば、呼び出し元は、配列 (空の配列であっても) を取得することが保証されていることを認識し、呼び出しごとに null をチェックする必要はありません。

編集:空の配列を返すことに関するリンク:

http://wesnerm.blogs.com/net_undocumented/2004/02/empty_arrays.html

于 2008-09-30T06:10:35.873 に答える
2

これはあなたの質問に対する直接の答えではありません。

アレイがやや有害であると考えられる理由をお読みください。この場合、IList <string>を返し、コードを少し再構築することをお勧めします。

IList<string> GetTheStuff() {
    List<string> s = new List<string>();
    if( somePredicate() ) {
        // imagine we load some data or something
    }
    return s;
}

このようにして、呼び出し元は空の戻り値を気にする必要がありません。


編集:返されたリストを編集できないようにする場合は、リストをReadOnlyCollection内にラップできます。最後の行をに変更するだけです。また、このベストプラクティスも検討します。

    return new ReadOnlyCollection(s);
于 2008-09-30T06:18:32.423 に答える
0

私の理解が正しければ、文字列配列に少量のメモリが割り当てられます。あなたのコードは基本的にジェネリック リストを作成する必要があるのに、なぜそれを返さないのでしょうか?

[編集] null 値を返すバージョンのコードを削除しました。この状況でnullの戻り値に反対する他の回答は、より良いアドバイスのようです[/編集]

List<string> GetTheStuff()
{
   List<string> s = new List<string();
   if (somePredicarte())
   {
      // more code
   }
   return s;
}
于 2008-09-30T06:15:43.663 に答える