c# - Why does this LINQ grouping have a count of 3 instead of 2? -


given following classes:

public class weekofyear : iequatable<weekofyear>, icomparable<weekofyear> {     private readonly datetime datetime;     private readonly dayofweek firstdayofweek;      public weekofyear(datetime datetime)         : this(datetime, dayofweek.sunday)     {     }      public weekofyear(datetime datetime, dayofweek firstdayofweek)     {         this.datetime = datetime;         this.firstdayofweek = firstdayofweek;     }      public int year     {                 {             return datetime.year;         }     }      public int week     {                 {             return cultureinfo.currentculture.calendar.getweekofyear(datetime, calendarweekrule.firstday, firstdayofweek);         }     }      public bool equals(weekofyear other)     {         return year == other.year && week == other.week;     }      public int compareto(weekofyear other)     {         if (year > other.year || year == other.year && week > other.week)         {             return 1;         }         if (equals(other))         {             return 0;         }         return -1;     }      public override string tostring()     {         return string.format("week of {0}", datetime.firstdayofweek(firstdayofweek).tostring("mmmm dd, yyyy"));     } }  public class weekofyearcomparer : iequalitycomparer<weekofyear>, icomparer<weekofyear> {     public bool equals(weekofyear x, weekofyear y)     {         return x.equals(y);     }      public int gethashcode(weekofyear weekofyear)     {         return weekofyear.gethashcode();     }      public int compare(weekofyear x, weekofyear y)     {         return x.compareto(y);     } } 

this test fails (unexpectedly):

[test] public void fails() {     var dates = new list<datetime>                     {                         new datetime(2012, 1, 1),                         new datetime(2012, 2, 1),                         new datetime(2012, 1, 1)                     };      ienumerable<igrouping<weekofyear, datetime>> groups = dates.groupby(date => new weekofyear(date), new weekofyearcomparer());      assert.that(groups.count(), is.equalto(2)); // count 3 } 

and test passes (expectedly):

[test] public void works() {     var dates = new list<datetime>                     {                         new datetime(2012, 1, 1),                         new datetime(2012, 2, 1),                         new datetime(2012, 1, 1)                     };      var groups = dates.groupby(         date =>             {                 var weekofyear = new weekofyear(date);                 return new { weekofyear.year, weekofyear.week };             });      assert.that(groups.count(), is.equalto(2)); } 

why first test result in count of 3?

the first part of equality check done via hash-code; must provide valid hash-code implementation (for why, see why important override gethashcode when equals method overridden?). comparer this, defers object:

public int gethashcode(weekofyear weekofyear) {     return weekofyear.gethashcode(); } 

and object does not provide valid hash-code. suitable implementation inside weekofyear like:

public bool equals(weekofyear other) {     return other != null && year == other.year && week == other.week; } public override bool equals(object obj) {     return equals(obj weekofyear); } public override int gethashcode() { // exploit number of weeks in year     return (year.gethashcode()*52) + week.gethashcode(); } 

noting i've provided override equality too.

actually, since object provides code here, there no benefit in custom comparer; remove weekofyearcomparer completely, default behaviour suitable equality/comparison operations on underlying type:

var groups = dates.groupby(date => new weekofyear(date)); 

Comments

Popular posts from this blog

delphi - How to convert bitmaps to video? -

jasper reports - Fixed header in Excel using JasperReports -

python - ('The SQL contains 0 parameter markers, but 50 parameters were supplied', 'HY000') or TypeError: 'tuple' object is not callable -