here is the object code:
public class DlpItem : IEqualityComparer<DlpItem>
{
public string Text { get; set; }
public int Id { get; set; }
public DlpItem(int pId)
{
Text = string.Empty;
Id = pId;
}
public override bool Equals(object obj)
{
return Id == (obj as DlpItem).Id;
}
public bool Equals(DlpItem a, DlpItem b)
{
return a.Id == b.Id;
}
public int GetHashCode(DlpItem item)
{
return Id.GetHashCode();
}
}
And I have two lists as follows:
var list1 = new List<DlpItem>();
list1.Add(new DlpItem(1));
list1.Add(new DlpItem(2));
var list2 = new List<DlpItem>();
list2.Add(new DlpItem(1));
list2.Add(new DlpItem(2));
var delItems = list1.Except(list2).ToList<DlpItem>();
delItems always has both items in it. What am I missing here?
EDIT: Code now implements IEquatable
public class DlpItem : IEqualityComparer<DlpItem>, IEquatable<DlpItem>
{
public string Text { get; set; }
public int Id { get; set; }
public override bool Equals(object obj)
{
return Id - (obj as DlpItem).Id == 0;
}
public bool Equals(DlpItem a, DlpItem b)
{
return a.Id == b.Id;
}
public bool Equals(DlpItem item)
{
return item != null && Id == item.Id;
}
public int GetHashCode(DlpItem item)
{
return Id.GetHashCode();
}
}
In your example, you don't actually add anything to list2... a simple enough mistake, but there is a more significant issue:
It needs to be IEquatable<T> not an IEqualityComparer<T>; also, you might want to ensure the hashcode can't change; most simply by making Id read-only:
public class DlpItem : IEquatable<DlpItem>
{
public string Text { get; set; }
private readonly int id;
public int Id { get { return id; } }
public DlpItem(int id)
{
Text = "";
this.id = id;
}
public override bool Equals(object obj)
{
return Equals(obj as DlpItem);
}
public bool Equals(DlpItem other)
{
return other != null && this.Id == other.Id;
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
}
Related
There is a class of Products:
public class ProductWithFeatures
{
public string Name { get; set; }
public ICollection<Feature> Features { get; set; }
}
public class Feature
{
public int Id { get; set; }
public Feature(int Id)
{
this.Id = Id;
}
}
I want to write an IEqualityComparer for this (i already have one for Feature).
The one for Feature is like this:
public class FeatureComparer : IEqualityComparer<Feature>
{
public bool Equals(Feature x, Feature y)
{
return x.Id == y.Id;
}
public int GetHashCode(Feature obj)
{
return obj.Id;
}
}
And what i wrote so far on the other one is this:
public class ProductComparer : IEqualityComparer<LinqHomework.ProductWithFeatures>
{
public bool Equals(ProductWithFeatures x, ProductWithFeatures y)
{
return x.Name == y.Name && LinqHomework.FeatureComparer.Equals(x.Features, y.Features);
}
public int GetHashCode(ProductWithFeatures obj)
{
}
}
I can't find an answer anywhere about this. Does anybody know how to write it?
Two ProductWithFeaturess are equal if they have the same name, and have the same features in the same order.
public class ProductComparer : IEqualityComparer<LinqHomework.ProductWithFeatures>
{
public bool Equals(ProductWithFeatures x, ProductWithFeatures y)
{
return x.Name == y.Name && x.Features.SequenceEqual(y.Features, new LinqHomework.FeatureComparer());
}
public int GetHashCode(ProductWithFeatures obj)
{
int hash = obj.Name.GetHashCode();
var featureComparer = new LinqHomework.FeatureComparer();
foreach (var feature in obj.Features)
{
hash = hash * 23 + featureComparer.GetHashCode(feature);
}
return hash;
}
}
This is a simple approach, which can be improved in a number of ways.
First, let's give our FeatureComparer a Default property, so we don't need to keep creating new instances:
public class FeatureComparer : IEqualityComparer<Feature>
{
public static FeatureComparer Default { get; } = new FeatureComparer();
// ... as before
}
This lets us write:
public class ProductComparer : IEqualityComparer<LinqHomework.ProductWithFeatures>
{
public bool Equals(ProductWithFeatures x, ProductWithFeatures y)
{
return x.Name == y.Name && x.Features.SequenceEqual(y.Features, LinqHomework.FeatureComparer.Default);
}
public int GetHashCode(ProductWithFeatures obj)
{
int hash = obj.Name.GetHashCode();
foreach (var feature in obj.Features)
{
hash = hash * 23 + LinqHomework.FeatureComparer.Default.GetHashCode(feature);
}
return hash;
}
}
We're also not handling the case where our methods are passed null, or the name of a feature is null, so let's deal with those. We can also test whether x and y are the same object in Equals.
We'll also do the integer operations in an unchecked block in case it overflows (and the assembly is compiled with /checked).
Note that we use ReferenceEquals instead of ==, in case you end up implementing the == operator in your types.
public class ProductComparer : IEqualityComparer<LinqHomework.ProductWithFeatures>
{
public bool Equals(ProductWithFeatures x, ProductWithFeatures y)
{
if (ReferenceEquals(x, y))
return true;
if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
return false;
if (x.Name != y.Name)
return false;
if (ReferenceEquals(x.Features, y.Features))
return true;
if (ReferenceEquals(x.Features, null) || ReferenceEquals(y.Features, null))
return false;
if (!x.Features.SequenceEquals(y.Features, LinqHomework.FeatureComparer.Default))
return false;
return true;
}
public int GetHashCode(ProductWithFeatures obj)
{
if (ReferenceEquals(obj, null))
return 0;
unchecked
{
int hash = obj.Name?.GetHashCode() ?? 0;
if (!ReferenceEquals(obj.Features, null))
{
foreach (var feature in obj.Features)
{
hash = hash * 23 + LinqHomework.FeatureComparer.Default.GetHashCode(feature);
}
return hash;
}
}
}
}
It's really up to you. I personally would go for something like
public int GetHashCode( ProductWithFeatures obj )
{
string toHash = obj.Name;
foreach( var feature in obj.Features )
toHash += feature.GetHashCode();
return toHash.GetHashCode();
}
It's not the nicest code ever, but it does what it's supposed to do.
I have several classes with id property of the same type int?:
public class Person {
public int? id { get; set; }
}
public class Project {
public int? id { get; set; }
}
// etc...
When writing code it happened that I compared semantically wrong types:
if (person.id == project.id), and of course there was no warning until I found the bug.
How could I create some kind of underlying type enforcement, or even better, a compiler warning, or something like that, that warns me not everything looks o.k.?
I can think of creating an Equals(Person p) { return p.id == this.id } but I'd prefer some other mechanism that could be used more 'freely'.
You need to override Equals and GetHashCode to be able to compare objects directly.
Try like this:
public sealed class Person : IEquatable<Person>
{
private readonly int? _id;
public int? Id { get { return _id; } }
public Person(int? id)
{
_id = id;
}
public override bool Equals(object obj)
{
if (obj is Person)
return Equals((Person)obj);
return false;
}
public bool Equals(Person obj)
{
if (obj == null) return false;
if (!EqualityComparer<int?>.Default.Equals(_id, obj._id)) return false;
return true;
}
public override int GetHashCode()
{
int hash = 0;
hash ^= EqualityComparer<int?>.Default.GetHashCode(_id);
return hash;
}
public override string ToString()
{
return String.Format("{{ Id = {0} }}", _id);
}
public static bool operator ==(Person left, Person right)
{
if (object.ReferenceEquals(left, null))
{
return object.ReferenceEquals(right, null);
}
return left.Equals(right);
}
public static bool operator !=(Person left, Person right)
{
return !(left == right);
}
}
public sealed class Project : IEquatable<Project>
{
private readonly int? _id;
public int? Id { get { return _id; } }
public Project(int? id)
{
_id = id;
}
public override bool Equals(object obj)
{
if (obj is Project)
return Equals((Project)obj);
return false;
}
public bool Equals(Project obj)
{
if (obj == null) return false;
if (!EqualityComparer<int?>.Default.Equals(_id, obj._id)) return false;
return true;
}
public override int GetHashCode()
{
int hash = 0;
hash ^= EqualityComparer<int?>.Default.GetHashCode(_id);
return hash;
}
public override string ToString()
{
return String.Format("{{ Id = {0} }}", _id);
}
public static bool operator ==(Project left, Project right)
{
if (object.ReferenceEquals(left, null))
{
return object.ReferenceEquals(right, null);
}
return left.Equals(right);
}
public static bool operator !=(Project left, Project right)
{
return !(left == right);
}
}
I also implemented IEquatable<Person> and == and != for good measure.
Now you can write person1 == this if this is a Person, but you would have a compiler error if this were a Project.
This is what tests are for. This is why you should write tests. Tests should pick up on these kind of errors.
But if you really want to go overkill, create a custom struct to store your IDs:
public struct Id<T> {
public int? ID { get; }
public static implicit operator Id<T>(int id) {
return new Id<T>(id);
}
public Id(int? id) { ID = id; }
public static bool operator ==(Id<T> lhs, Id<T> rhs) {
return lhs.ID == rhs.ID;
}
public static bool operator !=(Id<T> lhs, Id<T> rhs) {
return lhs.ID != rhs.ID;
}
}
// usage:
public class Person {
public Id<Person> Id { get; set; }
}
public class Project {
public Id<Project> Id { get; set; }
}
Whenever you try to compare Person.Id with Project.Id, the compiler will give you an error because you are comparing Id<Project> and Id<Person>.
i have this certain Key in my Dictionary:
public class KKey
{
public string Name { get; set; }
public Guid Guid { get; set; }
}
Dictionary<KKey, string> myDictionary = new Dictionary<KKey, string>();
problem was everytime I generate new Guid, this condition wouldnt work:
if (false == myDictionary.TryGetValue(key, out keyobj))
because Guid key was new ...
Now my question was, how can i make condition that verify if Kkey.Name was already added, then dont add?
You either need to create a custom comparer or have your class override Equals and GetHashCode
Option 1: Comparer
sealed class NameEqualityComparer : IEqualityComparer<KKey>
{
public bool Equals(KKey x, KKey y)
{
return string.Equals(x.Name, y.Name);
}
public int GetHashCode(KKey obj)
{
return (obj.Name != null ? obj.Name.GetHashCode() : 0);
}
}
Dictionary<KKey, string> myDictionary = new Dictionary<KKey, string>(new NameEqualityComparer());
Option 2: Override
public class KKey : IEquatable<KKey>
{
public string Name { get; set; }
public Guid Guid { get; set; }
public bool Equals(KKey other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return string.Equals(Name, other.Name);
}
public override bool Equals(object obj)
{
return Equals(obj as KKey);
}
public override int GetHashCode()
{
return (Name != null ? Name.GetHashCode() : 0);
}
}
Dictionary<KKey, string> myDictionary = new Dictionary<KKey, string>();
can also be
bool alreadyAdded = keywordList.Any(n => n.Key.Name == name);
if (!alreadyAdded) { }
I have a collection of objects where I want to find distinct values based on several properties.
I could do this:
var distinct = myValues.GroupBy(p => new { A = p.P1, B = p.P2 });
But I want to encapsulate the equality sementics. Something like this:
public interface IKey<T>
{
bool KeyEquals(T other);
}
public class MyClass : IKey<MyClass>
{
public string P1 { get; set; }
public string P2 { get; set; }
public bool KeyEquals(MyClass other)
{
if(object.ReferenceEquals(this, other)
return true;
if(other == null)
return false;
return this.P1 == other.P1 && this.P2 == other.P2;
}
}
Is there an O(N) way to get distinct values using my KeyEquals function?
If you can't change MyClass, you can implement an IEqualityComparer:
class MyClassComparer : IEqualityComparer<MyClass>
{
public bool Equals(MyClass m1, MyClass m2)
{
return m1.KeyEquals(m2);
}
public int GetHashCode(MyClass m)
{
return (m.P1.GetHashCode() *23 ) + (m.P2.GetHashCode() * 17);
}
}
And pass it to GroupBy
var distinct = myValues.GroupBy(p => p, new MyClassComparer());
I have a simple class called User :
public class User
{
public int ID { get; set; }
public int MI { get; set; }
public User(int id, int mi)
{
ID = ID;
MI = mi;
}
}
And later on, I have a HashSet of Users that I want to get the ID's from and assign to a in HashSet as follows :
HashSet<Users> _users = new HashSet<>();
//code where several User objects are assigned to _users
HashSet<int> _usersIDs = new HashSet<int>();
_usersIDs = _users.Select("ID")
But this doesn't work, how can I successfully assigned all of the int ID's in _users to a new HashSet?
You can do:
HashSet<int> _usersIDs = new HashSet<int>(_users.Select(user=> user.ID));
But you should override GetHashCode for your User class if you are going to use it in a HashSet<T> and possibily Eqauls as well like:
public class User
{
protected bool Equals(User other)
{
return ID == other.ID && MI == other.MI;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((User) obj);
}
public override int GetHashCode()
{
unchecked
{
return (ID*397) ^ MI;
}
}
public int ID { get; set; }
public int MI { get; set; }
public User(int id, int mi)
{
ID = id; //based on #Jonesy comment
MI = mi;
}
}