2012-03-09 6 views
7

को देखते हुए निम्नलिखित वर्गों:यह LINQ समूहिंग के बजाय 2 की बजाय 3 की गणना क्यों है?

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? देखें)। आपका comparer ऐसा कर सकता है, लेकिन यह वस्तु को defers:

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

धन्यवाद, मार्क। मैंने सोचा कि यह हैश कोड के साथ कुछ करने के लिए हो सकता है लेकिन अनिश्चित था। यह निश्चित रूप से समस्या हल हो गया। चीयर्स। –