Union lists by content - c#

I have 2 lists:
list1<a>
list2<b>
I would like to merge these 2 lists without duplicates according to a property exist in a and b.
To do so I have to override the methods Equals and GetHashCode.
public override bool Equals(object otherInstance)
{
MyClass instance = otherInstance as MyClass ;
return (instance != null) ? GetGuid.Equals(instance .GetGuid) : false;
}
public override int GetHashCode()
{
return GetGuid.GetHashCode();
}
However, I cannot inherit class A and class B from a common base class, since they already extend other classes and multiple inheritance is invalid using C#.
Any ideas?

There are number of ways you can get this done. I'll take a shortcut. This should work if you're using .Net 4.0 and above, If not please drop a comment I'll update my answer.
List<Class1> list1 = new List<Class1>();
list1.Add(new Class1());
List<Class2> list2 = new List<Class2>();
list2.Add(new Class2());
var unionList = list1.Cast<object>()
.Union(list2.Cast<object>(), new DynamicComparer())
.ToList();
internal class DynamicComparer : IEqualityComparer<object>
{
public new bool Equals(object x, object y)
{
dynamic dx = x;
dynamic dy = y;
return dx.Guid == dy.Guid;
}
public int GetHashCode(object obj)
{
dynamic dobj = obj;
return dobj.Guid.GetHashCode();
}
}
internal class Class1
{
public Guid Guid { get; set; }
}
internal class Class2
{
public Guid Guid { get; set; }
}
Since I don't know how you implemented GetHashCode and Equals I just given a example. This should give you a idea of how to start.
How does it works: We're using IEqualityComparer<T> interface to get the job done which is used to implement custom comparison rules to find object equiality. but the problem is we don't have a common interface or base class for Class1 and Class2 only way to represent it as common is via System.Object so we implement IEqualityComparer<object>.
Then implementation of Equals is pretty simple it just converts the parameters to dynamic which leverages dynamic feature of .Net4.0. then simply compare the properties or fields dynamically(no compile time failure) in this case we compare Guid property and that's all.
Worth noting that this may fail if the property doesn't exist in runtime rather than compile time.
Hope this helps.

You have a couple good options here. Although C# doesn't support multiple inheritance, it does support some pretty handy interfaces here. You could use IComparable or IEquatable to hand-roll your own solution, or use the LINQ Distinct method.
Another option is to use LINQ Distinct and pass an IEqualityComparer.
Have the A and B implement a common interface which contains Prop. Then:
LINQ Distinct
class MyClassComparer<T> : IEqualityComparer<MyClass>
{
public bool Equals(object otherInstance)
{
MyClass instance = otherInstance as MyClass ;
return (instance != null) ? GetGuid.Equals(instance .GetGuid) : false;
}
public int GetHashCode()
{
return GetGuid.GetHashCode();
}
}
}
listA.AddRange(listB); //Need to be List<ICommonInterface> Common Interface
var distinctList = listA.Distinct(new MyClassComparer<T>());

Related

How to correctly implement this IComparable?

I have problems wit hthe implementation of a generic sorting algorithm.
We need to implement quicksort and selection sort, and a class which should be sortable using these functions. The functions should be generic, and thus work on other classes as well.
I tested the quicksort. It works perfectly on a List. However, when trying to execute it on my own comparable class, it says:
There is no implicit reference conversion from 'SNIP' to 'System.IComparable'
Do you guys have any idea what the problem can be?
Here is my comparable class:
public class SNIP : IComparable
{
private long lCost { get; set; }
public SNIP(long lCost)
{
this.lCost = lCost;
}
public int CompareTo(object obj)
{
if (obj == null) return 1;
SNIP oOtherPlank = obj as SNIP;
if (oOtherPlank != null)
return this.lCost.CompareTo(oOtherPlank.lCost);
else
throw new ArgumentException("Can only compare SNIPs.");
}
}
Thanks in advance!
Thanks to #Sweeper, the comparable is now fixed.
Like he said, I had to define my class better:
public class SNIP : IComparable<SNIP>
{
private long lCost { get; set; }
public SNIP(long lCost)
{
this.lCost = lCost;
}
public int CompareTo(SNIP obj)
{
if (obj == null) return 1;
SNIP oOtherSnip= obj as SNIP;
if (oOtherSnip!= null)
return this.lCost.CompareTo(oOtherSnip.lCost);
else
throw new ArgumentException("Can only compare SNIPs.");
}
}
It is also important to note that this only works when the argument of the CompareTo method is actually of the right class. I tried defining the class as IComparable before, but it didn't work because the argument in CompareTo was set to an object. By changing both the CompareTo header and the class header, the problem is fixed and the sorting now works.
Thanks a lot :-)
You can implement generic IComparable<SNIP>, not IComparable which is very simple: this is always geater than null and if we compare with not null other we should check lCost.
public class SNIP : IComparable<SNIP>
{
private long lCost { get; set; }
public SNIP(long lCost)
{
this.lCost = lCost;
}
public int CompareTo(SNIP other) => other is null
? 1
: lCost.CompareTo(other.lCost);
}
then you can sort: note that since List<SNIP> is generic, the generic IComparable<SNIP> will be used on sorting.
List<SNIP> list = new List<SNIP>()
{
new SNIP(5),
new SNIP(1),
new SNIP(3),
};
list.Sort();

How to Implement IEquatable<MyType> in a class that [duplicate]

This question already has answers here:
LINQ Intersect not returning items
(2 answers)
Closed 1 year ago.
I need to have instances of a DispenseFile class that inherits from DispenseEntity that implements IDispenseEntity use a custom equality for purposes of comparing elements in List.
My interface is:
public interface IDispenseEntity : IEquatable<IDispenseEntity>
{
byte[] Id { get; set; }
List<byte[]> Key { get; }
List<byte[]> ParentKey { get; set; }
double Volume { get; set; }
public bool DispenseEnabled { get; set; }
string Name { get; set; }
}
My DispenseEntity class:
public class DispenseEntity : IDispenseEntity, IEquatable<IDispenseEntity>
{
//Other properties and methods
//I've tried - this implements IEquatable:
public bool Equals(IDispenseEntity other)
{
return Id.SequenceEqual(other.Id);
}
//I've tried - this overrides default:
public override bool Equals(object obj)
{
return Id.SequenceEqual((obj as IDispenseEntity).Id);
}
}
My DispenseFile class:
public class DispenseFile : DispenseEntity, IParent, IOutputable, IDispenseFile
{
//Other methods and properties
public override bool Equals(object obj)
{
return base.Equals(obj);
}
}
No matter what I use in DispenseEntity class for Equals() method it does not get used when I try:
List<IDispenseEntity> before = _aList;
List<IDispenseEntity> after = _bList;
var intersect = before.Intersect(after).ToList();
The intersect list has zero elements.
I am absolutely positive that both _aList and _bList have an instance of DispenseFile that inherits DispenseEntity who implements IDispenseEntity. I have written test code that finds the only DispenseFile entity in _aList and finds a single instance of DispenseFile in _bList. Both of these instances are created separately and have identical property Id ( new byte[] {1,2,3,4} )
I have tried overriding Equals. I have tried adding IEquatable to the base class and implementing the equals and GetHashCode and those don't get used.
The problem has to be me, what am I doing wrong?
You don't even need to implement IEquatable anything; overriding GetHashCode and Equals will suffice
public class DispenseEntity : IDispenseEntity
{
...
public override int GetHashCode()
{
return new BigInteger(Id).GetHashCode(); //or your choice
}
public override bool Equals(object obj)
{
return obj is DispenseEntity x && x.Id.SequenceEqual(Id);
}
}
No matter what I use in DispenseEntity class for Equals() method it does not get used
Indeed it might not; a précis on Intersect:
Intersect uses a HashSet; the contents of list B are added to a set, then the contents of A are enumerated and attempted to be added to the set. If the Add returns false, indicating the item is already known, the item from A is yielded. At the end of the operation all those items from A that are also in B have been returned
Now, for a HashSet using the default hashcode provider to decide if it contains some object X it first gets X's hashcode and looks in the objects it knows about to see if there are any other objects with the same hashcode.
If there are no known objects with the same hash, then it deems that the set doesn't contain the object.
If there are object(s) with the same hash, then it uses Equals to decide if the colliding object truly is the same. If you rely on the default hashcode implementation from object it's essentially the memory address of the item, so the only way you'd get the same hashcode is if list A and list B share an instance
Long story short, if you don't override GetHashCode you'll get an empty set result because when all of _bList is added to the set, and then all of _aList is enumerated and the set is asked "got this one?" the answer is always "no" - Equals never needs to be used to figure out if instances are the same because the hashes are always different, and the intersection is { } (nothing)
..but if you've got Equals and GetHashCode overridden, you should be good to go. You could even override GetHashCode to return 1 (don't; it would be terribly inefficient) and you'd see Equals used (a lot)..

Intersect doesn't work between Lists

I have a little strange problem. I use Visual Studio and I am developing a project with C#.
I have two custom classes "Attr" and "FD" and I use lists that includes their objects e.g.
List<Attr> attrList = new List<Attr>();
List<FD> fdList = new List<FD>();
So when I try to find the intersection of two lists the result is not what I expect. To make it more simple I tried to Intersect similar Objects and the result is wrong again. What is going wrong?
This is the fd. It is an object of class FD.
This is the ff which is also an object of FD class.
As you can see these object contains exactly the same values.
The method GetLeft() returns a list that contains objects of class Attr.
So when I try to find the intersection between those two lists (fd.GetLeft() and ff.GetLeft() ) the result is nothing (it should be a list that contains an Attr object "A").
What did I miss?
P.S. These screenshots are from the debugg mode in Visual Studio.
In order to use Intersect I suggest implementing IEqualityComparer<T>, something like this :
public class FD
{
public string Name { get; set; }
}
static void Main()
{
List<FD> fdList1 = new List<FD>();
fdList1.Add(new FD { Name = "a" });
List<FD> fdList2 = new List<FD>();
fdList2.Add(new FD { Name = "a" });
IEnumerable<FD> fd = fdList1.Intersect<FD>(fdList2, new ComparerFd()).ToList();
}
And the CamparerFd should be like this :
public class ComparerFd : IEqualityComparer<FD>
{
public bool Equals(FD x, FD y)
{
return x.Name == y.Name;
}
public int GetHashCode(FD obj)
{
if(obj == null) return 0;
return obj.Name.GetHashCode();//Or whatever way to get hash code
}
}
If you created your own class, and did not override the Equals-method in that class, the Intersect-method will only compare the references of the objects, and not the properties.
Take the following, really simple class:
class MyClass
{
int Value { get; set; }
public MyClass(int value)
{
this.Value = value;
}
}
Now, create two lists, with both containing one object. The properties of the objects are the same, but the instances are not:
var list1 = new List<MyClass>
{
new MyClass(5)
};
var list2 = new List<MyClass>
{
new MyClass(5)
};
So the following will happen:
list1[0].Equals(list2[0]); // false
list1.Intersect(list2); // No matches
If you want these methods to compare the properties of your MyClass-objects, implement IEqualityComparer<MyClass>, e.g. change the classes signature to:
class MyClass : IEqualityComparer<MyClass>
{
..
}
Alternatively, you can just override Equals and GetHashCode, as then these methods will be called as default IEqualityComparer.
See the this answer on how to properly override Equals and GetHashCode.

Emulating F# `with` keyword in C#

Is there a way to emulate F#'s with keyword in C#? I know it will likely not be as elegant, but I'd like to know if there's any way to handle creating new immutable copies of data structures.
Records in F# are detailed here.
Here's an example of what I'm trying to do. We'll create "immutable" views of data via interfaces, while maintaining mutability in concrete classes. This lets us mutate locally (while working) and then return an immutable interface. This is what we're handling immutability in C#.
public interface IThing
{
double A { get; }
double B { get; }
}
public class Thing : IThing
{
double A { get; set; }
double B { get; set; }
}
However, when it comes time to make a change to the data, it's not very type (or mutability!) safe to cast it back and forth, and it's also a real pain to manually translate each property of the class into a new instance. What if we add a new one? Do I have to go track down each manipulation? I don't want to create future headache when I really only need what I had before, but with [some change].
Example:
// ...
IThing item = MethodThatDoesWork();
// Now I want to change it... how? This is ugly and error/change prone:
IThing changed = new Thing {
A = item.A,
B = 1.5
};
// ...
What are sound strategies for accomplishing this? What have you used in the past?
As there is no syntactic sugar I am aware of you'll have to either:
do it by hand (see below)
use some reflection/automapper (not a fan of this)
use some AOP techniques (neither a fan of those)
At least this is what I can think of right now.
I don't think the last two are a good idea because you bring on the big machinery to solve a very easy problem.
Yes when you have thousands of data-structures you might rethink this, but if you only have a couple of them I would not use it.
So what's left is basically smart-constructors and stuff like this - here is a simple example of how you could do it (note that you don't really need all of this - pick and choose) - it's basically missusing null/nullable to look for what you need - better options to this might be overloads or something like an Option<T> data-type but for now I think you get it:
class MyData
{
private readonly int _intField;
private readonly string _stringField;
public MyData(int intField, string stringField)
{
_intField = intField;
_stringField = stringField;
}
public MyData With(int? intValue = null, string stringValue = null)
{
return new MyData(
intValue ?? _intField,
stringValue ?? _stringField);
}
// should obviously be put into an extension-class of some sort
public static MyData With(/*this*/ MyData from, int? intValue = null, string stringValue = null)
{
return from.With(intValue, stringValue);
}
public int IntField
{
get { return _intField; }
}
public string StringField
{
get { return _stringField; }
}
}
To add to Carsten's correct answer, there's no way to do this in C# because it's not in the language. In F#, it's a language feature, where succinct record declaration syntax expands to quite a bit of IL. C# doesn't have that language feature (yet).
This is one of the reasons I no longer like to work in C#, because there's too much overhead compared to doing the same thing in F#. Still, sometimes I have to work in C# for one reason or the other, and when that happens, I bite the bullet and write the records by hand.
As an example, the entire AtomEventSource library is written in C#, but with immutable records. Here's an abbreviated example of the AtomLink class:
public class AtomLink : IXmlWritable
{
private readonly string rel;
private readonly Uri href;
public AtomLink(string rel, Uri href)
{
if (rel == null)
throw new ArgumentNullException("rel");
if (href == null)
throw new ArgumentNullException("href");
this.rel = rel;
this.href = href;
}
public string Rel
{
get { return this.rel; }
}
public Uri Href
{
get { return this.href; }
}
public AtomLink WithRel(string newRel)
{
return new AtomLink(newRel, this.href);
}
public AtomLink WithHref(Uri newHref)
{
return new AtomLink(this.rel, newHref);
}
public override bool Equals(object obj)
{
var other = obj as AtomLink;
if (other != null)
return object.Equals(this.rel, other.rel)
&& object.Equals(this.href, other.href);
return base.Equals(obj);
}
public override int GetHashCode()
{
return
this.Rel.GetHashCode() ^
this.Href.GetHashCode();
}
// Additional members removed for clarity.
}
Apart from the overhead of having to type all of this, it's also been bothering me that if you're doing (dogmatic) Test-Driven Development (which you don't have to), you'd want to test these methods as well.
Using tools like AutoFixture and SemanticComparison, though, you can make it somewhat declarative. Here's an example from AtomLinkTests:
[Theory, AutoAtomData]
public void WithRelReturnsCorrectResult(
AtomLink sut,
string newRel)
{
AtomLink actual = sut.WithRel(newRel);
var expected = sut.AsSource().OfLikeness<AtomLink>()
.With(x => x.Rel).EqualsWhen(
(s, d) => object.Equals(newRel, d.Rel));
expected.ShouldEqual(actual);
}
Here, it's still relatively verbose, but you can easily refactor this to a generic method, so that each test case becomes a one-liner.
It's still a bother, so even if you're writing most of your code in C#, you might consider defining your immutable types in a separate F# library. Viewed from C#, F# records look like 'normal' immutable classes like AtomLink above. Contrary to some other F# types like discriminated unions, F# records are perfectly consumable from C#.
Here is my attempt at emulating immutable mutations in C# via concrete classes. Some magic via generics, which includes type safety!
class Program
{
static void Main(string[] args)
{
var r = new Random();
// A new class item
IDataItem item = new DataItem
{
A = r.NextDouble(),
B = r.NextDouble(),
C = r.NextDouble(),
D = r.NextDouble()
};
// Type hinting here helps with inference
// The resulting `newItem` is an "immutable" copy of the source item
IDataItem newItem = item.With((DataItem x) =>
{
x.A = 0;
x.C = 2;
});
// This won't even compile because Bonkers doesn't implement IDataItem!
// No more casting madness and runtime errors!
IBonkers newItem2 = item.With((Bonkers x) => { /* ... */ });
}
}
// A generic record interface to support copying, equality, etc...
public interface IRecord<T> : ICloneable,
IComparable,
IComparable<T>,
IEquatable<T>
{
}
// Immutable while abstract
public interface IDataItem : IRecord<IDataItem>
{
double A { get; }
double B { get; }
double C { get; }
double D { get; }
}
// Mutable while concrete
public class DataItem : IDataItem
{
public double A { get; set; }
public double B { get; set; }
public double C { get; set; }
public double D { get; set; }
public object Clone()
{
// Obviously you'd want to be more explicit in some cases (internal reference types, etc...)
return this.MemberwiseClone();
}
public int CompareTo(object obj)
{
// Boilerplate...
throw new NotImplementedException();
}
public int CompareTo(IDataItem other)
{
// Boilerplate...
throw new NotImplementedException();
}
public bool Equals(IDataItem other)
{
// Boilerplate...
throw new NotImplementedException();
}
}
// Extension method(s) in a static class!
public static class Extensions
{
// Generic magic helps you accept an interface, but work with a concrete type
// Note how the concrete type must implement the provided interface! Type safety!
public static TInterface With<TInterface, TConcrete>(this TInterface item, Action<TConcrete> fn)
where TInterface : class, ICloneable
where TConcrete : class, TInterface
{
var n = (TInterface)item.Clone() as TConcrete;
fn(n);
return n;
}
}
// A sample interface to show type safety via generics
public interface IBonkers : IRecord<IBonkers> { }
// A sample class to show type safety via generics
public class Bonkers : IBonkers
{
public object Clone()
{
throw new NotImplementedException();
}
public int CompareTo(object obj)
{
throw new NotImplementedException();
}
public int CompareTo(IBonkers other)
{
throw new NotImplementedException();
}
public bool Equals(IBonkers other)
{
throw new NotImplementedException();
}
}

How does HashSet compare elements for equality?

I have a class that is IComparable:
public class a : IComparable
{
public int Id { get; set; }
public string Name { get; set; }
public a(int id)
{
this.Id = id;
}
public int CompareTo(object obj)
{
return this.Id.CompareTo(((a)obj).Id);
}
}
When I add a list of object of this class to a hash set:
a a1 = new a(1);
a a2 = new a(2);
HashSet<a> ha = new HashSet<a>();
ha.add(a1);
ha.add(a2);
ha.add(a1);
Everything is fine and ha.count is 2, but:
a a1 = new a(1);
a a2 = new a(2);
HashSet<a> ha = new HashSet<a>();
ha.add(a1);
ha.add(a2);
ha.add(new a(1));
Now ha.count is 3.
Why doesn't HashSet respect a's CompareTo method.
Is HashSet the best way to have a list of unique objects?
It uses an IEqualityComparer<T> (EqualityComparer<T>.Default unless you specify a different one on construction).
When you add an element to the set, it will find the hash code using IEqualityComparer<T>.GetHashCode, and store both the hash code and the element (after checking whether the element is already in the set, of course).
To look an element up, it will first use the IEqualityComparer<T>.GetHashCode to find the hash code, then for all elements with the same hash code, it will use IEqualityComparer<T>.Equals to compare for actual equality.
That means you have two options:
Pass a custom IEqualityComparer<T> into the constructor. This is the best option if you can't modify the T itself, or if you want a non-default equality relation (e.g. "all users with a negative user ID are considered equal"). This is almost never implemented on the type itself (i.e. Foo doesn't implement IEqualityComparer<Foo>) but in a separate type which is only used for comparisons.
Implement equality in the type itself, by overriding GetHashCode and Equals(object). Ideally, implement IEquatable<T> in the type as well, particularly if it's a value type. These methods will be called by the default equality comparer.
Note how none of this is in terms of an ordered comparison - which makes sense, as there are certainly situations where you can easily specify equality but not a total ordering. This is all the same as Dictionary<TKey, TValue>, basically.
If you want a set which uses ordering instead of just equality comparisons, you should use SortedSet<T> from .NET 4 - which allows you to specify an IComparer<T> instead of an IEqualityComparer<T>. This will use IComparer<T>.Compare - which will delegate to IComparable<T>.CompareTo or IComparable.CompareTo if you're using Comparer<T>.Default.
Here's clarification on a part of the answer that's been left unsaid: The object type of your HashSet<T> doesn't have to implement IEqualityComparer<T> but instead just has to override Object.GetHashCode() and Object.Equals(Object obj).
Instead of this:
public class a : IEqualityComparer<a>
{
public int GetHashCode(a obj) { /* Implementation */ }
public bool Equals(a obj1, a obj2) { /* Implementation */ }
}
You do this:
public class a
{
public override int GetHashCode() { /* Implementation */ }
public override bool Equals(object obj) { /* Implementation */ }
}
It is subtle, but this tripped me up for the better part of a day trying to get HashSet to function the way it is intended. And like others have said, HashSet<a> will end up calling a.GetHashCode() and a.Equals(obj) as necessary when working with the set.
HashSet uses Equals and GetHashCode().
CompareTo is for ordered sets.
If you want unique objects, but you don't care about their iteration order, HashSet<T> is typically the best choice.
constructor HashSet receive object what implement IEqualityComparer for adding new object.
if you whant use method in HashSet you nead overrride Equals, GetHashCode
namespace HashSet
{
public class Employe
{
public Employe() {
}
public string Name { get; set; }
public override string ToString() {
return Name;
}
public override bool Equals(object obj) {
return this.Name.Equals(((Employe)obj).Name);
}
public override int GetHashCode() {
return this.Name.GetHashCode();
}
}
class EmployeComparer : IEqualityComparer<Employe>
{
public bool Equals(Employe x, Employe y)
{
return x.Name.Trim().ToLower().Equals(y.Name.Trim().ToLower());
}
public int GetHashCode(Employe obj)
{
return obj.Name.GetHashCode();
}
}
class Program
{
static void Main(string[] args)
{
HashSet<Employe> hashSet = new HashSet<Employe>(new EmployeComparer());
hashSet.Add(new Employe() { Name = "Nik" });
hashSet.Add(new Employe() { Name = "Rob" });
hashSet.Add(new Employe() { Name = "Joe" });
Display(hashSet);
hashSet.Add(new Employe() { Name = "Rob" });
Display(hashSet);
HashSet<Employe> hashSetB = new HashSet<Employe>(new EmployeComparer());
hashSetB.Add(new Employe() { Name = "Max" });
hashSetB.Add(new Employe() { Name = "Solomon" });
hashSetB.Add(new Employe() { Name = "Werter" });
hashSetB.Add(new Employe() { Name = "Rob" });
Display(hashSetB);
var union = hashSet.Union<Employe>(hashSetB).ToList();
Display(union);
var inter = hashSet.Intersect<Employe>(hashSetB).ToList();
Display(inter);
var except = hashSet.Except<Employe>(hashSetB).ToList();
Display(except);
Console.ReadKey();
}
static void Display(HashSet<Employe> hashSet)
{
if (hashSet.Count == 0)
{
Console.Write("Collection is Empty");
return;
}
foreach (var item in hashSet)
{
Console.Write("{0}, ", item);
}
Console.Write("\n");
}
static void Display(List<Employe> list)
{
if (list.Count == 0)
{
Console.WriteLine("Collection is Empty");
return;
}
foreach (var item in list)
{
Console.Write("{0}, ", item);
}
Console.Write("\n");
}
}
}
I came here looking for answers, but found that all the answers had too much info or not enough, so here is my answer...
Since you've created a custom class you need to implement GetHashCode and Equals. In this example I will use a class Student instead of a because it's easier to follow and doesn't violate any naming conventions. Here is what the implementations look like:
public override bool Equals(object obj)
{
return obj is Student student && Id == student.Id;
}
public override int GetHashCode()
{
return HashCode.Combine(Id);
}
I stumbled across this article from Microsoft that gives an incredibly easy way to implement these if you're using Visual Studio. In case it's helpful to anyone else, here are complete steps for using a custom data type in a HashSet using Visual Studio:
Given a class Student with 2 simple properties and an initializer
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public Student(int id)
{
this.Id = id;
}
}
To Implement IComparable, add : IComparable<Student> like so:
public class Student : IComparable<Student>
You will see a red squiggly appear with an error message saying your class doesn't implement IComparable. Click on suggestions or press Alt+Enter and use the suggestion to implement it.
You will see the method generated. You can then write your own implementation like below:
public int CompareTo(Student student)
{
return this.Id.CompareTo(student.Id);
}
In the above implementation only the Id property is compared, name is ignored. Next right-click in your code and select Quick actions and refactorings, then Generate Equals and GetHashCode
A window will pop up where you can select which properties to use for hashing and even implement IEquitable if you'd like:
Here is the generated code:
public class Student : IComparable<Student>, IEquatable<Student> {
...
public override bool Equals(object obj)
{
return Equals(obj as Student);
}
public bool Equals(Student other)
{
return other != null && Id == other.Id;
}
public override int GetHashCode()
{
return HashCode.Combine(Id);
}
}
Now if you try to add a duplicate item like shown below it will be skipped:
static void Main(string[] args)
{
Student s1 = new Student(1);
Student s2 = new Student(2);
HashSet<Student> hs = new HashSet<Student>();
hs.Add(s1);
hs.Add(s2);
hs.Add(new Student(1)); //will be skipped
hs.Add(new Student(3));
}
You can now use .Contains like so:
for (int i = 0; i <= 4; i++)
{
if (hs.Contains(new Student(i)))
{
Console.WriteLine($#"Set contains student with Id {i}");
}
else
{
Console.WriteLine($#"Set does NOT contain a student with Id {i}");
}
}
Output:

Categories