简体   繁体   中英

Implement a custom hashcode on a third party class

I'm using a Line class in VisualStudio C# project from a third party (no access to the source) My code is generating hundreds/thousands of line objects containing duplicates and I need to store them in some kind of collection (List, HashSet) for further processing (drawing on the screen). The Line class has StartPoint and EndPoint properties of type Point among others. For the purposes of what I need to do line orientation does not matter and if the start and end points are the same the two Line objects are the same. The class I'm using however behaves differently and two Line objects are considered unique even if they have identical start/end points. (GetHashCode returns different values) Question is - how do I implement a custom IComparer or GetHashCode routines for the third party class in order to be able to use either the HashSet structure or List.Distinct() functionality?

Thanks Ilian and Hasan, that did the trick Quick follow up question if u dont mind: I wasnt sure whether to use a List or a HashSet to hold my data so I tried and timed both. The results show one being orders of magnitude slower than the other. Any insights as to what is going on?

class LineComparer : IEqualityComparer<Line>
{
    public bool Equals(Line l1, Line l2)
    {
        if (l1.EndPoint == l2.EndPoint && l1.StartPoint == l2.StartPoint) return true;
        if (l1.EndPoint == l2.StartPoint && l1.StartPoint == l2.EndPoint) return true;
        return false;
    }
    public int GetHashCode(Line line) => line.StartPoint.GetHashCode() ^ line.EndPoint.GetHashCode();
}

and this is my code for HashSet:

var timer = new Stopwatch();

timer.Start();

var result = new HashSet(new LineComparer());

GenerateAndStore20000Lines();

timer.Stop(); Ed.WriteMessage($"\nGenerated {result.Count} lines, time with hashset: {timer.ElapsedMilliseconds}");

return result;

Results: * Time with HashSet: 1302 * Time with HashSet: 1328 * Time with HashSet: 1314 * Time with HashSet: 1311 * Time with HashSet: 1303

code with List:

var timer = new Stopwatch(); timer.Start();

var result = new List();

GenerateAndStore20000Lines();

timer.Stop(); Ed.WriteMessage($"\nGenerated {result.Count} lines, time with List: {timer.ElapsedMilliseconds}");

return result.Distinct(new LineComparer());

Results:

  • Generated 20002 lines, time with List: 26
  • Generated 20002 lines, time with List: 11
  • Generated 20002 lines, time with List: 14
  • Generated 20002 lines, time with List: 12
  • Generated 20002 lines, time with List: 12

(sorry for the bad formatting, but this interface is driving me crazy... giving up)

Use the overloads with IEqualityComparer (which you will have to implement).

See this for HashSet .

I assume, you mean Enumerable.Distinct . Use this .

I am just expanding upon @Ilian answer. Tried to comment as much as possible so I believe it is better for the code to do the talking:)

// Mock 3rd Party point
public class ThirdPartyPoint {

}

// Mock 3rd party line
public class ThirdPartyLine {

    public ThirdPartyPoint StartPoint { get; set; }
    public ThirdPartyPoint EndPoint { get; set; }

}

// This class implements IEqualityComparer<ThirdPartyLine>, which compares
// ThirdPartyLine's equality. THis class will be passed as a ctor arument to HashSet<T>
public class CompareLines : IEqualityComparer<ThirdPartyLine> {

    public bool Equals(ThirdPartyLine x, ThirdPartyLine y) {
        // Here check for the equality of the start and end points.
        // I asuumed the following but do not know how the eaulity is implemented in your library.
        return x.EndPoint == y.EndPoint && x.StartPoint == y.StartPoint;
    }

    public int GetHashCode(ThirdPartyLine obj) {
        // Implement an algortihm which must return same hashcode for objects considered the same.
        // I am not sure about the Point class hashcode but I am jsut assuming the following.
        return obj.StartPoint.GetHashCode() ^ obj.EndPoint.GetHashCode();
    }

}


private static void Main(string[] args) {
    // Hashset to hold lines
    var hashSet = new HashSet<ThirdPartyLine>(new Compare());
    // start point
    var starPoint = new ThirdPartyPoint();
    // end point
    var endPoint = new ThirdPartyPoint();

    // Lines with same start and end points
    var line1 = new ThirdPartyLine {
        StartPoint = starPoint,
        EndPoint = endPoint
    };

    var line2 = new ThirdPartyLine {
        StartPoint = starPoint,
        EndPoint = endPoint
    };


    // Check count first
    hashSet.Add(line1);
    var count = hashSet.Count;

    // Check count second, still 1
    hashSet.Add(line2);
    count = hashSet.Count;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM