I've implemented a custom IEqualityComparer for EventLogEntry.
public class EventLogEntryListComparison :
IEqualityComparer<List<EventLogEntry>>,
IEqualityComparer<EventLogEntry>
For IEqualityComparer<List<EventLogEntry>>
, the GetHashCode function is very simple.
public int GetHashCode(List<EventLogEntry> obj)
{
return obj.Sum(entry => 23 * GetHashCode(entry));
}
However, this throws an OverflowException for certain entries.
"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()
After trying to get the same error whilst debugging and couldn't in the immediate window, I changed the code to this and bye bye OverflowException?
int total = 0;
foreach (var eventLogEntry in obj)
{
total += GetHashCode(eventLogEntry);
}
return total;
How is it that LINQ's Sum function behaving differently?
Edit 2
Thanks to a few comments, the corrected and intended GetHashCode function is now as follows,
public int GetHashCode(List<EventLogEntry> obj)
{
return unchecked(obj.Aggregate(17,
(accumulate, entry) =>
accumulate * 23 + GetHashCode(entry)));
}
LINQ's Enumerable.Sum(...)
methods perform the additions inside a checked
block. This means that they deliberately throw an exception if the sum overflows.
Your sum is not inside a checked
block, so whether or not it throws an exception depends on... whether it is called from inside a checked
block, or a property on the assembly I believe.
It's because of the different behavior of Assemblies compiled in C# and the implementation of Enumerable.Sum
.
If you compile an Assembly in C#, by default all additions are performed in unchecked
mode, which is why you don't get an overflow in your last example. If you want the runtime to throw on overflows, you need to use checked
blocks (of course for your hash, you don't want that, so C#'s default behavior is fine).
By contrast, Enumerable.Sum
is meant to calculate a sum, and usually, you don't want sums to overflow. That's why Enumerable.Sum
performs its calculations in checked
mode, which throws an exception if the sum overflows.
if you're computing a Hash Code, you may not want to use Sum anyways. Using Xor (^
) will provide the same results and may even spread your hash codes out more than a sum will. Try this method:
public int GetHashCode(List<EventLogEntry> obj)
{
int total = 0;
foreach (var eventLogEntry in obj)
{
total ^= GetHashCode(eventLogEntry);
}
return total;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With