Frage

I was surprised when I found that my override GetHashCode() method of class Foo is called when I am using foreach to travel in IEnumerable<Foo>. But it doesn't happens in others cases. Why?

Some parts of real code:

var allVolumeImagesInvolvedInMerge = volumeChainsToMerge
    .SelectMany(x => x);

var allVolumeImagesNotInvolvedInMerge = allVolumeImagesWithinCell
    .Except(allVolumeImagesInvolvedInMerge)
    .Where(vi => volumeImagesNotAllowedToDelete.ContainsFast(vi) == false);

var volumeImagesCandidatesForDeletion = allVolumeImagesNotInvolvedInMerge
    .Where(x => driverVolumeIds.Contains(x.DriverVolumeId));

var groupedVolumeImagesCandidatesForDeletion = volumeImagesCandidatesForDeletion
    .GroupBy(vi => vi.DriverVolumeId);

// here GetHashCode is called
foreach (var group in groupedVolumeImagesCandidatesForDeletion)
{
   ...
}
War es hilfreich?

Lösung

I assume that your IEnumerable<Foo> is not a collection type like Foo[] or List<Foo> but a linq query which uses deferred execution. So when you use foreach (or ToList, Any etc) you'll execute the query which causes all involved methods to be executed.

Maybe you are using GroupBy, Distinct, Intersect,Except, Join or other methods that use your overridden GetHashCode and Equals(if GetHashCode returns an equal value).

Here's a simple example which reproduces it:

public class Foo
{
    public int ID { get; set; }

    public override int GetHashCode()
    {
        return ID.GetHashCode();
    }
    public override bool Equals(object obj)
    {
        Foo f2 = obj as Foo;
        if (f2 == null) return false;
        return ID == f2.ID;
    }
}

Now this simple linq query demonstrates that GetHashCode is executed in the foreach due to the deferred execution of the Enumerable extension method:

IEnumerable<Foo> list1 = new List<Foo>() { new Foo { ID = 1 }, new Foo { ID = 2 }, new Foo { ID = 3 } };
IEnumerable<Foo> list2 = new List<Foo>() { new Foo { ID = 2 }, new Foo { ID = 3}, new Foo { ID = 4 } };
IEnumerable<Foo> inBoth = list1.Intersect(list2);

// now GetHashCode will be executed (not at list1.Intersect(list2))
foreach (Foo fDup in inBoth)
    Console.WriteLine(fDup.ID);

Here's a demo: http://ideone.com/ekttH3

Output:

before Intersect
after Intersect
in GetHashCode, ID=2
in GetHashCode, ID=3
in GetHashCode, ID=4
in GetHashCode, ID=1
in GetHashCode, ID=2
in Equals, ID=2
in foreach, ID=2
in GetHashCode, ID=3
in Equals, ID=3
in foreach, ID=3
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top