2012-03-09 29 views
7

考慮以下類:爲什麼這個LINQ分組的計數是3而不是2?

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 
    { 
     get 
     { 
      return dateTime.Year; 
     } 
    } 

    public int Week 
    { 
     get 
     { 
      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); 
    } 
} 

此測試失敗(意外):

[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 is 3 
} 

而這個測試通過(果然):

[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)); 
} 

爲什麼第一次測試結果計數3?

回答

10

等式檢查的第一部分是通過散列碼完成的;你必須提供了一個有效的哈希碼實現(爲什麼,請參閱Why is it important to override GetHashCode when Equals method is overridden?)。你比較器能做到這一點,但它推遲到對象:

public int GetHashCode(WeekOfYear weekOfYear) 
{ 
    return weekOfYear.GetHashCode(); 
} 

和對象提供有效的哈希碼。裏面WeekOfYear合適的實現將是這樣的:

public bool Equals(WeekOfYear other) 
{ 
    return other != null && Year == other.Year && Week == other.Week; 
} 
public override bool Equals(object obj) 
{ 
    return Equals(obj as WeekOfYear); 
} 
public override int GetHashCode() 
{ // exploit number of weeks in year 
    return (Year.GetHashCode()*52) + Week.GetHashCode(); 
} 

注意到我還提供了平等的override了。其實,由於你的對象在這裏提供了所有的代碼,所以在自定義比較器中沒有任何好處;您可以完全刪除WeekOfYearComparer,因爲默認行爲是在基礎類型上查找適當的相等/比較操作:

var groups = dates.GroupBy(date => new WeekOfYear(date)); 
+0

謝謝Marc。我認爲它可能與散列碼有關,但不確定。這絕對解決了這個問題。乾杯。 – 2012-03-09 06:20:44

相關問題