12

List<T>.ForEach()拡張メソッドが内部でforループを実装する理由がわかりません。これにより、コレクションが変更される可能性が広がります。この場合、通常foreachは例外をスローするので、確かForEach()に同じように反応する必要がありますか?

何らかの理由でコレクションを変更する必要がある場合は、コレクションをforループで手動で反復処理する必要がありますか?

ここでとの間には、意味上の矛盾が少しあるようforeachですList<T>.ForEach()

私は何かが足りないのですか?

4

3 に答える 3

5

List.ForEachは、MSDNの定義に従っているためです。

リストの各要素に対して指定されたアクションを実行します。

つまりAction、要素に対して実行されると、要素またはコレクション自体が変更される可能性があります。この場合、これを購入する他の方法はありません(可能であれば、コストのかかるクローンコレクションを作成しない場合)。次に、単純なを使用しforます。

の反復中にコレクションを変更するforeachと、当然、例外が発生します。

于 2012-07-15T15:57:42.047 に答える
5

foreachC#言語要素です。C#のルールに従って再生されます。

List<T>.ForEach.NETFrameworkメソッドです。foreach存在しない.NETのルールで再生します。

これは「言語とフレームワーク」の混乱の例です。フレームワークメソッドは多くの言語で機能する必要があり、言語は(通常)矛盾したセマンティクスを持っています。

この「言語とフレームワーク」の混乱のもう1つの例は、Enumerable.Cast.net3と.NET3.5の間の重大な変更です。.NET 3では、CastC#セマンティクスを使用していました。.net 3.5では、.netセマンティクスを使用するように変更されました。

于 2012-07-15T16:00:09.710 に答える
5

BCLチームのメンバーだけが確実に教えてくれList<T>.ForEachますが、リストを変更できるのはおそらく単なる見落としでした。

まず、DavidBの答えは私には意味がありません。ループList<T>内でリストを変更するかどうかをチェックし、変更する場合はスローするのは、C#ではありません。使用している言語とは何の関係もありません。foreachInvalidOperationException

次に、ドキュメントに次の警告があります。

Action <T>デリゲートの本体で基になるコレクションを変更することはサポートされておらず、未定義の動作を引き起こします。

ForEachBCLチームが、未定義の動作をするような単純な方法を望んでいた可能性は低いと思います。

第3に、.NET 4.5以降、デリゲートがリストを変更した場合はList<T>.ForEach スローします。InvalidOperationExceptionプログラムが古い動作に依存している場合、ターゲットの.NET 4.5に再コンパイルすると、プログラムは動作を停止します。マイクロソフトがこの重大な変更を喜んで受け入れるという事実は、元の動作が意図されておらず、信頼されるべきではないことを強く示唆しています。

List<T>.ForEach参考までに、リファレンスソースから直接.NET4.0に実装する方法は次のとおりです。

public void ForEach(Action<T> action) {
    if( action == null) {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
    }
    Contract.EndContractBlock();

    for(int i = 0 ; i < _size; i++) {
        action(_items[i]);
    }
}

そして、これが.NET4.5でどのように変更されたかを示します。

public void ForEach(Action<T> action) {
    if( action == null) {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
    }
    Contract.EndContractBlock();

    int version = _version;

    for(int i = 0 ; i < _size; i++) {
        if (version != _version && BinaryCompatibility.TargetsAtLeast_Desktop_V4_5) {
            break;
        }
        action(_items[i]);
    }

    if (version != _version && BinaryCompatibility.TargetsAtLeast_Desktop_V4_5)
        ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
}
于 2013-05-04T15:20:15.777 に答える