Assume we have a complex structure containing some other complex types, even possibly containing itself e.g.
public class Box {
public string Name { get; set; }
public List<Box> Boxes { get; set; }
public List<Item> Items { get; set; }
public SomeOtherComplexType BlaBla { get; set; }
...
}
And we need to have a possibility to compare these complex types.
Since overriding object.Equals() will be of a poor perfomance when it comes to generic types
I see a lot of usages of IEquatable<>.
The problem is that using IEquatable<> uglifies the code very much mixing the business logic and the technical part of overriding the Equeals() and GetHashCode() methods.
And the more bigger our structure is - the larger overridden methods are.
It looks awful and oblige to do the same for all inner types.
public class Box : IEquatable<Box> {
public string Name { get; set; }
public List<Box> Boxes { get; set; }
public List<Item> Items { get; set; }
public SomeOtherComplexType BlaBla { get; set; }
public bool Equals(Box other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return
(
Name == other.Name||
Name != null &&
Name.Equals(other.Name)
) &&
(
Boxes == other.Boxes ||
Boxes != null &&
Boxes.SequenceEqual(other.Boxes)
) &&
(
Items == other.Items ||
Items != null &&
Items.SequenceEqual(other.Items)
) &&
(
BlaBla == other.BlaBla ||
BlaBla != null &&
BlaBla.SequenceEqual(other.BlaBla)
);
}
public override int GetHashCode()
{
unchecked
{
var hashCode = 14;
if (Name != null)
hashCode = hashCode * 14 + Name.GetHashCode();
if (Boxes != null)
hashCode = hashCode * 14 + Boxes.GetHashCode();
if (Items != null)
hashCode = hashCode * 14 + Items.GetHashCode();
if (BlaBla!= null)
hashCode = hashCode * 14 + BlaBla.GetHashCode();
return hashCode;
}
}
}
Are there any ways of making it less worse or maybe some other alternatives.
Maybe some ways to hide this implementation and make it as much automated as possible?
There are only 4 fields, but what if we are having some complex types with ~30 types in it, with some nested types etc??
Using something like hybrid composite design pattern seems to be even more complex to implement.
First, IEquatable<T> has no impact on performance for reference types. I would drop all your Equals() logic and be done with it. Unless you really do not want equality by reference.
Then, the only sane solution is to introduce a Guid for the object's identity:
public class Box : IEquatable<Box>
{
public Box(Guid id)
{
Id = id;
}
public Guid Id { get; }
public override bool Equals(object obj) => obj is Box box && Equals(box);
public bool Equals(Box other) => Id == other.Id;
public override int GetHashCode() => Id.GetHashCode();
}
Related
The problem I am facing is that I want to compare two objects of type CustomerEntity. Sometimes both of the objects being compared match on EntityID, EntityType, and EntityName, so I need a way to compare EntityObject as well.
public class CustomerEntity
{
public string EntityID { get; set; }
public string EntityName { get; set; }
public string EntityType { get; set; }
public object EntityObject { get; set; }
}
The value of EntityObject could be one of several different types of objects depending on what the value of EntityType is. All of the types which EntityObject could be all implement IComparable, but the basic Object class does not, so how do I go about comparing them?
Below is an example of a class which EntityObject could be:
public class EquipmentEntity : IEquatable<EquipmentEntity>, IComparable<EquipmentEntity>
{
public string Manufacturer { get; set; }
public string Model { get; set; }
public int ModelYear { get; set; }
public override bool Equals(object obj)
{
return EqualityTester(this, obj as EquipmentEntity);
}
public bool Equals(EquipmentEntity other)
{
return EqualityTester(this, other);
}
public override int GetHashCode()
{
return (Manufacturer + Model + ModelYear.ToString()).GetHashCode();
}
private static bool EqualityTester(EquipmentEntity a, EquipmentEntity b)
{
if (a.Manufacturer.ToLower().Equals(b.Manufacturer.ToLower()) == false ) { return false; }
if (a.Model.ToLower().Equals(b.Model.ToLower()) == false ) { return false; }
if (a.ModelYear.Equals(b.ModelYear) == false ) { return false; }
return true;
}
public int CompareTo(object obj)
{
return ComparisonTester(this, obj as EquipmentEntity);
}
public int CompareTo(EquipmentEntity other)
{
return ComparisonTester(this, other);
}
private static int ComparisonTester(EquipmentEntity a, EquipmentEntity b)
{
if (a is null && b != null) { return -1; }
if (a != null && b is null) { return 1; }
if (a is null && b is null) { return 0; }
return (a.Manufacturer + a.Model + a.ModelYear.ToString()).CompareTo(b.Manufacturer + b.Model + b.ModelYear.ToString());
}
}
I figured it out. I created an abstract class which has abstract methods for the implementation of the IComparable (CompareTo) and IEquitable (Equals, GetHashCode) methods.
public abstract class EntityWrapper : IComparable<EntityWrapper>, IEquatable<EntityWrapper>
{
public abstract override bool Equals(Object obj);
public abstract bool Equals(EntityWrapper other);
public abstract override int GetHashCode();
public abstract int CompareTo(Object obj);
public abstract int CompareTo(EntityWrapper other);
}
The actual entity object classes provide their own implementations of these methods which are called by generic Linq methods such as .Equals() and .Sort(). Hope this helps anyone else who runs into this issue.
I have a Pharmacy class which contains lots of properties, and a Pharmacy is declared as unique by having the ID property as a key.
I have code in place which fetches all rows in a table from MySQL back to Pharmacy objects with their properties.
I want compare two List<Pharmacy> objects for the entries in them and check if the same ID exists in both tables, if it doesn't exist then add it to a new List<Pharmacy. If the ID exists in both but the data in the objects differs, then save that object to a new List<Pharmacy aswell.
This is how the class looks like.
public class Pharmacy
{
[Key]
public string Tunniste { get; set; }
public string Lyhenne { get; set; }
public string PitkaNimi { get; set; }
public string YlempiYksikko { get; set; }
public string Hierarkiataso { get; set; }
public string VoimassaoloAlkaa { get; set; }
public string VoimassaoloPaattyy { get; set; }
...
}
It's in Finnish but I hope you can live with that.
Here's how I've tried to check if they're identical.
for (int i = 0; i != pharmacyListFromArchive.Count; i++)
{
if (pharmacyListFromArchive[i].Equals(pharmacyListFromNew[i]))
{
Console.WriteLine("Objects are identical.");
}
else
{
Console.WriteLine("Objects are NOT identical. {0} - {1}", pharmacyListFromArchive[i].Tunniste, pharmacyListFromNew[i].Tunniste);
}
}
But when I run that, none of the objects register as identical even though they are identical in data. How can I work around this?
The standard implementation of Equals checks only for reference equality. What is the default behavior of Equals Method?
You can either override the behavior of Equals. Guidelines for Overriding Equals() and Operator == (C# Programming Guide).
public class Pharmacy {
// fields ...
public override bool Equals(object obj) {
// If parameter is null return false.
if (obj == null) {
return false;
}
// If parameter cannot be cast to Pharmacy return false.
Pharmacy p = obj as Pharmacy;
if ((System.Object)p == null) {
return false;
}
// Return true if the fields match:
return p.Lyhenne == this.Lyhenne &&
p.PitkaNimi == this.PitkaNimi
// && etc...
;
}
public override int GetHashCode() {
return Lyhenne.GetHashCode() ^ PitkaNimi.GetHashCode() /* ^ etc ... */;
}
}
Or you implement a custom IEqualityComparer IEqualityComparer Interface. This might be preferable if your ORM Mapper relies on the default equals (like Entity Framework does).
I have a list of custom class called List<Notifications>.
The class is below:
public class Notification
{
public enum Type {
Promotion,
Other
}
public string ID { get; set; }
public string Headline { get; set; }
public string Detail { get; set; }
public Type NotificationType { get; set; }
}
Before adding an instance of the Notification class to my custom list, I want to check if it is already in the list.
What is the best way to achieve this?
You can use 1.) Contains, but then you have to override Equals (+ GethashCode).
bool contains = list.Contains(someNotificationInstance);
For example:
public class Notification
{
public enum Type {
Promotion,
Other
}
public string ID { get; set; }
public string Headline { get; set; }
public string Detail { get; set; }
public Type NotificationType { get; set; }
public override bool Equals(object obj)
{
return obj is Notification && string.Equals(ID, ((Notification)obj).ID);
}
public override int GetHashCode()
{
return ID == null ? 0 : ID.GetHashCode();
}
}
2.) another option is to provide a custom IEqualityComparer<Notification> for Contains:
public class NotificationComparer : IEqualityComparer<Notification>
{
public bool Equals(Notification x, Notification y)
{
return x.ID == y.ID;
}
public int GetHashCode(Notification obj)
{
return obj.ID == null ? 0 : obj.ID.GetHashCode();
}
}
On this way you don't need to modify the original class. You can use it in this way:
bool contains = list.Contains(someInstance, new NotificationComparer());
3.) Probably the easiest approach is using Enumerable.Any:
bool contains = list.Any(n => someInstance.ID == n.ID);
4.) The most efficient approach is using a set if no duplicates are allowed in the collection anyway. Then you can use the first or second approaches for a HashSet<T>:
var set = new HashSet<Notification>(new NotificationComparer());
set.Add(instance1);
bool contains = !set.Add(instance2);
You can check it with Contains method.
if (!mylist.Select(l => l.ID).Contains(mynewid)) {
var item = new Notifcation();
item.ID = mynewid;
item..... // fill the rest
mylist.Add(item);
}
Maybe a better approch would be use of Dictionary.
I've read many posts here about exception mentioned in the title. Typically this exception means that somewhere I mapped to fields to one entity. I spent pretty much time looking at my mappings, but still can't find what's wrong. Could you please help me with understanding what've been done wrong here:
public class CustomerSegment : ModelEntity, IEntityDescriptor
{
public const string Standart = "Standard";
public virtual string Name { get; set; }
public virtual NetworkNHMapped Network { get; set; }
public virtual string GetDescriptor()
{
return Name;
}
}
public class CustomerSegmentMap : ClassMap<CustomerSegment>
{
public CustomerSegmentMap()
{
Table("NetworkProperty");
Id(x => x.Id).Column("NetworkPropertyId");
Map(x => x.Name).Column("PropertyName");
References(x => x.Network).Column("NetworkId");
}
}
}
The exception occurs when I'm trying get all CustomerSegment entities from DB.
The code of other entities:
public class NetworkNHMapped : ModelEntity
{
[StringLength(50)]
public virtual string Name { get; set; }
public virtual int NetworkOwnerId { get; set; }
public virtual int NetworkTypeId { get; set; }
public virtual RepairShop.NetworkType NetworkType { get { return (RepairShop.NetworkType)NetworkTypeId; } }
}
public class NetworkNewMap : ClassMap<NetworkNHMapped>
{
public NetworkNewMap()
{
Table("Network");
Id(x => x.Id, "NetworkId");
Map(x => x.Name, "NetworkName");
Map(x => x.NetworkOwnerId, "NetworkOwnerId");
Map(x => x.NetworkTypeId, "NetworkType");
}
}
And base ModelEntity:
public virtual int Id { get; set; }
public override int GetHashCode()
{
if (!IsPersisted())
{
return base.GetHashCode();
}
unchecked
{
int result = GetObjectRealType(this).GetHashCode();
result = 42 * result + Id.GetHashCode();
return result;
}
}
public override bool Equals(object other)
{
if (ReferenceEquals(this, other))
{
return true;
}
if ((other == null) || !(other is ModelEntity))
{
return false;
}
var thisType = GetObjectRealType(this);
var otherType = GetObjectRealType(other);
if (thisType != otherType)
return false;
if (Id.Equals(default(long)) && (other as ModelEntity).Id.Equals(default(long)))
{
return base.Equals(other);
}
return Id == (other as ModelEntity).Id;
}
public static bool operator ==(ModelEntity entity1, ModelEntity entity2)
{
var obj1 = (object)entity1;
if (obj1 == null && ((object)entity2) == null)
return true;
return obj1 != null && entity1.Equals(entity2);
}
public static bool operator !=(ModelEntity entity1, ModelEntity entity2)
{
return !(entity1 == entity2);
}
public virtual bool IsPersisted()
{
return Id > 0;
}
protected static Type GetObjectRealType(object obj)
{
return (obj is INHibernateProxy) ? NHibernateUtil.GetClass(obj) : obj.GetType();
}
}
The first thing I would do is to look at the XML files that are being generated, this shows if you have duplicate mappings to the same property.
If you are using NH and ModelMapper then simply make a call to WriteAllXmlMapping e.g.:-
var mapper = new ModelMapper();
mapper.AddMappings(typeof(CmsMeta).Assembly.GetTypes());
//This will write all the XML into the bin/mappings folder
mapper.CompileMappingForEachExplicitlyAddedEntity().WriteAllXmlMapping();
If you are using Fluent NHibernate then look at this blog post to export your XML.
After you have generated the XML I bet you will find duplicate mappings! Otherwise see this blog post, it is possible if you are exposing a foreign-key as well as using a many-to-one ... to the related entity in the mapping file. If this is the case add insert="false" and update="false" to the foreign key property and run again.
Either way generate the XML and take a look, if you can't see it post the relevant XML files to your question and we will take a look.
I have a type like:
class Order
{
public List<IItem> AllItems { get; set; }
public string Name { get; set; }
public double TotalPurchases { get; set; }
public long Amount { get; set; }
public int Code { get; set; }
}
I've implemented the IEquatable<T> interface to check if two objects of this type are same or not. The current Equals method looks like:
public virtual bool Equals(Order other)
{
if ((object)other == null)
{
return false;
}
return (this.AllItems.Equals(other.AllItems)
&& this.Name.Equals(other.Name)
&& this.TotalPurchases.Equals(other.TotalPurchases)
&& this.Amount.Equals(other.Amount))
&& this.Code.Equals(other.Code));
}
But I wish to implement this method in such a way that it dynamically checks for equality of all the existing properties (or maybe certain properties of this type) without explicitly writing the code for comparison checks as above.
Hope I was able to express my question with clarity. :)
Thanks!
You could write a custom attribute that attaches to the properties on your type which you want to be included in the comparision. Then in the Equals method you could reflect over the type and extract all the properties which have the attribute, and run a comparison on them dynamically.
Psuedo code:
[AttributeUsage(AttributeTarget.Property)]
class IncludeInComparisonAttribute : Attribute { }
class Order
{
List<AllItem> Items { get; set; }
[IncludeInComparison]
string Name { get; set; }
long Amount { get; set; }
[IncludeInComparison]
int Code { get; set; }
override bool Equals(Order other)
{
Type orderType = typeof(Order);
foreach (PropertyInfo property in orderType.GetProperties()
{
if (property.CustomAttributes.Includes(typeof(IncludeInComparisonAttribute))
{
object value1 = property.GetValue(this);
object value2 = propetty.GetValue(other);
if (value1.Equals(value2) == false)
return false;
}
}
return true;
}
}
It'll certianly need to be a bit more elaborate than that, but that should hopefully set you on the right track :)
Two Orders are considered the same if all their properties are equal. It's OK for the 4 properties Name/TotalPurchases/Amount/Code, their default comparers are exactly what you want. But for the property AllItems (whose type is List<IItem>), you must tell how they consider to be equal. Currently you are using reference equals that is incorrect. this.AllItems.Equals(other.AllItems) should be something like:
this.AllItems.SequenceEqual(other.AllItems, new ItemComparer())
And the ItemComparer is a class implements IEqualityComparer<Item> to tell how to check if two Items are equal.