11

EventLogEntry のカスタム IEqualityComparer を実装しました。

public class EventLogEntryListComparison :
    IEqualityComparer<List<EventLogEntry>>,
    IEqualityComparer<EventLogEntry>

の場合IEqualityComparer<List<EventLogEntry>>、GetHashCode 関数は非常に単純です。

public int GetHashCode(List<EventLogEntry> obj)
{
    return obj.Sum(entry => 23 * GetHashCode(entry));
}

ただし、これにより、特定のエントリに対して OverflowException がスローされます。

"Arithmetic operation resulted in an overflow."
   at System.Linq.Enumerable.Sum(IEnumerable`1 source)
   at System.Linq.Enumerable.Sum[TSource](IEnumerable`1 source, Func`2 selector)
   at <snip>.Diagnostics.EventLogAnalysis.EventLogEntryListComparison.GetHashCode(List`1 obj) in C:\dev\<snip>Diagnostics.EventLogAnalysis\EventLogEntryListComparison.cs:line 112
   at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
   at System.Collections.Generic.Dictionary`2.set_Item(TKey key, TValue value)
   at <snip>.Diagnostics.EventLogAnalysis.Program.AnalyseMachine(String validMachineName) in C:\dev\<snip>.Diagnostics.EventLogAnalysis\Program.cs:line 104
   at System.Threading.Tasks.Parallel.<>c__DisplayClass2d`2.<ForEachWorker>b__23(Int32 i)
   at System.Threading.Tasks.Parallel.<>c__DisplayClassf`1.<ForWorker>b__c()

デバッグ中に同じエラーを取得しようとして、すぐにウィンドウに表示できなかった後、コードをこれに変更し、さようならOverflowException?

int total = 0;
foreach (var eventLogEntry in obj)
{
    total += GetHashCode(eventLogEntry);
}

return total;

LINQ の Sum 関数の動作が異なるのはなぜですか?

編集 2

いくつかのコメントのおかげで、修正された目的の GetHashCode 関数は次のようになりました。

public int GetHashCode(List<EventLogEntry> obj)
{
    return unchecked(obj.Aggregate(17,
        (accumulate, entry) =>
        accumulate * 23 + GetHashCode(entry)));
}
4

3 に答える 3

9

LINQ のEnumerable.Sum(...)メソッドは、checkedブロック内で加算を実行します。これは、合計がオーバーフローした場合に意図的に例外をスローすることを意味します。

あなたの合計はcheckedブロック内にないため、例外をスローするかどうかは、checkedブロック内から呼び出されるか、アセンブリのプロパティから呼び出されるかによって異なります。

于 2012-06-14T13:15:33.220 に答える
5

これは、C# でコンパイルされたアセンブリと の実装の動作が異なるためですEnumerable.Sum

Assembly を C# でコンパイルすると、デフォルトではすべての追加がuncheckedモードで実行されるため、最後の例でオーバーフローが発生しません。ランタイムにオーバーフローをスローさせたい場合は、checkedブロックを使用する必要があります (もちろん、ハッシュにはブロックを使用したくないため、C# の既定の動作で問題ありません)。

対照的に、Enumerable.Sumは合計を計算するためのものであり、通常、合計がオーバーフローすることは望ましくありません。そのためEnumerable.Sum、モードで計算を実行しchecked、合計がオーバーフローした場合に例外をスローします。

于 2012-06-14T13:16:57.580 に答える
1

ハッシュ コードを計算している場合は、とにかく Sum を使用したくない場合があります。Xor ( ^) を使用しても同じ結果が得られますが、ハッシュ コードが合計以上に広がる場合もあります。この方法を試してください:

public int GetHashCode(List<EventLogEntry> obj)
{
    int total = 0;
    foreach (var eventLogEntry in obj)
    {
        total ^= GetHashCode(eventLogEntry);
    }

    return total;
}
于 2012-06-14T13:41:24.693 に答える