How to extract string representation of key object - c#

I'm storing items in a strongly typed IDictionary<TKey, TValue> such that the value also represents the key:
public class MyObject
{
public string Name { get; private set; }
public SectionId Section { get; private set; }
public MyObject(SectionId section, string name)
{
Section = section;
Name = name;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof(MyObject)) return false;
return Equals((MyObject)obj);
}
public override int GetHashCode()
{
unchecked
{
return (Name.ToLower().GetHashCode() * 397) ^ Section.GetHashCode();
}
}
}
In my presentation tier, I need to iterate through this Dictionary, adding each item to a ListBox control. I'm having a difficult time figuring out how to transform MyObject (which also acts as a key) into a string that the ListBox control can use as a value. Should I just make an explicit call to MyObject.GetHashCode() like this:
MyListBox.Add(new ListItem(myObject.Name, myObject.GetHashCode())

I would think of overriding the toString method and in here you will basically write code that will generate a meaningful string to be displayed in the ui
Hope I understood your question correctly.

Should I just make an explicit call to MyObject.GetHashCode()
No, GetHashCode() is:
Not guaranteed to give you a unique value, and
Going to be very difficult to reverse-engineer to produce a MyObject from.
Instead, each of your MyObjects should have some kind of unique identifier key. This can be an enum value or a number generated by an IDENTITY column in your database, or just a string that uniquely identifies each particular MyObject, and from which you can retrieve the MyObject from whatever collection or database you're using as a repository.
If there can only ever be a single MyObject with a given Name and Section, you could just combine the two: SectionId + ":" + Name. That way you can parse those two values out after the fact.

Related

IEquatable Documentation is confusing

Hi I am trying to add in IEquatable to my program and I have no clue if I need to add a unique id basicly and a hashcode? They are using the shd number as a unique id in the IEquatable but they give the value to it in the constructor and I asked on this site if the constructor was needed to look like it does in the documentation and I got a no. So now I am confused can someone give me an easier example of IEquatable then the documentation does? here is the link for the documentation https://learn.microsoft.com/en-us/dotnet/api/system.iequatable-1?view=netcore-3.1 I am just trying to get contains for a custom object list to work.
My code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DataConverter.Objects
{
public class Category : IEquatable<Category>
{
public string _Name { get; set; }
public string _Id { get; set;}
private string _HomeCategory { get; set; }
public string _prestaId { get; set; }
public bool _inUse { get; set; }
public Category(string Name, string Id, string HomeCategory, bool inUse)
{
_Name = Name;
_Id = Id;
_HomeCategory = HomeCategory;
_inUse = inUse;
}
public bool Equals(Category other)
{
if (other == null)
return false;
if()
}
}
}
IEquatable<T> is just an interface that tells you that the current type has value equality semantics, not reference equality. What the logic is behind what makes two distinct objets equal or not is completely dependant on the business logic of your application, not how you correctly or incorrectly implement the interface.
What makes two categories be the same in your domain? The same Id? Then your equals method would be:
public bool Equals(Category other)
{
if (other == null)
return false;
return _Id == other._Id;
}
The same Id and name?
public bool Equals(Category other)
{
if (other == null)
return false;
return _Id == other._Id &&
_Name == other._Name;
}
And so on. Its you who decides what makes two categories equal each other.
Now, about the hash code. The basic rule a hash code must comply with is: Two categories that are equal must have the same hash code. Do note that this does not mean that two categories with the same hash code are equal, hash codes can collide, there is no problem with that at all. Also, hash codes should not change if the object changes, so ideally they should be built upon data that can not be changed.
Ok, so how would you implement your hash code in your particular scenario?
Case 1:
public override int GetHashCode() => _Id.GetHashCode();
Case 2:
public override int GetHashCode() => _Id.GetHashCode() ^
_Name.GetHashCode();
Both hashes will be consistent with their Equals counterpart. The only problem is that they are built on potentially mutable data (both Id and Name have setters) so if any of those change, your haschcode would change too, and that is usually a bad idea.
Depending on your scenario you might have to deal with this problem differently. Is your data likely to mutate while performing any hash sensitive operations? Linq for example used hashes quite extensively...

Define equality based on class property to be used in HashSet

I have the following class:
public class OrderRule {
public OrderDirection Direction { get; set; }
public String Property { get; set; }
}
And an HashSet of it:
HashSet<OrderRule> rules = // ...
I need to OrderRules to be considered equal if the Property is equal.
How can I do this?
Since the specification for this equality is not coming from the OrderRule class, but your collection, use the constructor overload of the HashSet that accepts an IEqualityComparer.
public class MyOrderRuleComparer : EqualityComparer<OrderRule>
{
private IEqualityComparer<string> _c = EqualityComparer<string>.Default;
public override bool Equals(OrderRule l, OrderRule r)
{
return _c.Equals(l.Property, r.Property);
}
public override int GetHashCode(OrderRule rule)
{
return _c.GetHashCode(rule.Property);
}
}
...
HashSet<OrderRule> rules = new HashSet(new MyOrderRuleComparer());
Please note that by using OrderRule.Property as a key, you imply that it must not change after the instance is added to the set. This is why implementing IEquatable<OrderRule> could be the best approach depending on your developer team.
If I add two OrderRules with same Property but different Direction I
still need both to be considered equal
You could override Equals and GethashCode and/or implement IEquatable<OrderRule>:
public class OrderRule: IEquatable<OrderRule>
{
public OrderRule(string property)
{
this.Property = property;
}
public OrderDirection Direction { get; set; }
public String Property { get; }
public OrderRule Rule { get; set; }
public bool Equals(OrderRule other)
{
return (other != null && other.Property == this.Property);
}
public override int GetHashCode()
{
return Property?.GetHashCode() ?? int.MinValue;
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
if(ReferenceEquals(this, obj))
return true;
OrderRule other = obj as OrderRule;
return this.Equals(other);
}
}
Note that i've made the property read-only because you should not be able to modify a property or field that is used in GetHashCode.
Why?: "Guideline: the integer returned by GetHashCode should never change
Ideally, the hash code of a mutable object should be computed from only fields which cannot mutate, and therefore the hash value of an object is the same for its entire lifetime."
This value is f.e. used in a dictionary or HashSet to compute the hashcode. If it would change after the object was added it could no longer be found.

Optional Rest service parameters

Yesterday I came across a problem. I was asked to write a rest service which exposes all properties of a class. The consumers would all consume different properties and when submitting they would all send different subsets of them.
For example sake lets call the contract company.
class Company{
public string Address {get;set;}
public string CompanyNumber {get;set;}
public string Turnover {get;set;}
public string Employees {get;set;}
}
Lets say we have two known systems which would like to sync the Company class.
System 1 deals with accounting and wants to read Address and Update Turnover.
System 2 deals with hr and wants to read Address and update Employees.
Now normally when faced with this problem one would write lots of interfaces each with a tailored contract to suit the end system. However I have been told this is not what they want. Instead if the property is supplied in the JSON it must be set.
The problem is when deserialising the class Company from JSON if the property is not supplied the property is null, this will then when mapped to the database classes overwrite the data.
The solution I came up with was to create an Optional struct like Nullable but where null can also be a valid value.
When deserialising this struct can track if the property was set, and therefore provided in the JSON. I can then check this when mapping from the DTO to the database class and map only when the property is set.:)
https://dotnetfiddle.net/QmHtSW
public interface IOptional
{
bool HasBeenSet { get; }
}
public struct Optional<T> : IOptional
{
internal readonly T Val;
public Optional(T value)
{
Val = value;
HasBeenSet = true;
}
public Optional(Optional<T> value)
{
Val = value.Value;
HasBeenSet = value.HasBeenSet;
}
public bool HasBeenSet { get; }
public override bool Equals(object obj)
{
return obj.Equals(Val);
}
public override int GetHashCode()
{
return Val.GetHashCode();
}
public static implicit operator T(Optional<T> optional)
{
return optional.Val;
}
public static implicit operator Optional<T>(T optional)
{
return new Optional<T>(optional);
}
public T Value => Val;
}
and my AutoMapper mapping profile
CreateMap<TRestObject, TDomainObject>()
.ForAllMembers(mo=>mo.Condition(f =>
{
var opt = f.SourceValue as IOptional;
return opt==null || opt.HasBeenSet;
}));

Linq/Enumerable Any Vs Contains

I've solved a problem I was having but although I've found out how something works (or doesn't) I'm not clear on why.
As I'm the type of person who likes to know the "why" I'm hoping someone can explain:
I have list of items and associated comments, and I wanted to differentiate between admin comments and user comments, so I tried the following code:
User commentUser = userRepository.GetUserById(comment.userId);
Role commentUserRole = context.Roles.Single(x=>x.Name == "admin");
if(commentUser.Roles.Contains(commentUserRole)
{
//do stuff
}
else
{
// do other stuff
}
Stepping through the code showed that although it had the correct Role object, it didn't recognise the role in the commentUser.Roles
The code that eventually worked is:
if(commentUser.Roles.Any(x=>x.Name == "admin"))
{
//do stuff
}
I'm happy with this because it's less code and in my opinion cleaner, but I don't understand how contains didn't work.
Hoping someone can clear that up for me.
This is probably because you didn't override the equality comparisons (Equals, GetHashCode, operator==) on your Role class. Therefore, it was doing reference comparison, which really isn't the best idea, as if they're not the same object, it makes it think it's a different. You need to override those equality operators to provide value equality.
You have to override Equals (and always also GetHashCode then) if you want to use Contains. Otherwise Equals will just compare references.
So for example:
public class Role
{
public string RoleName{ get; set; }
public int RoleID{ get; set; }
// ...
public override bool Equals(object obj)
{
Role r2 = obj as Role;
if (r2 == null) return false;
return RoleID == r2.RoleID;
}
public override int GetHashCode()
{
return RoleID;
}
public override string ToString()
{
return RoleName;
}
}
Another option is to implement a custom IEqualityComparer<Role> for the overload of Enumerable.Contains:
public class RoleComparer : IEqualityComparer<Role>
{
public bool Equals(Role x, Role y)
{
return x.RoleID.Equals(y.RoleID);
}
public int GetHashCode(Role obj)
{
return obj.RoleID;
}
}
Use it in this way:
var comparer = new RoleComparer();
User commentUser = userRepository.GetUserById(comment.userId);
Role commentUserRole = context.Roles.Single(x=>x.Name == "admin");
if(commentUser.Roles.Contains(commentUserRole, comparer))
{
// ...
}
When using the Contains-method, you check if the the array Roles of the user-object contains the object you have retrieved from the database beforehand. Though the array contains an object for the role "admin" it does not contain the exact object you fetched before.
When using the Any-method you check if there is any role having the name "admin" - and that delivers the expected result.
To get the same result with the Contains-method implement the IEquatable<Role>-interface on the role-class and compare the name to check whether two instances have actually the same value.
It will be your equality comparison for a Role.
The object in commentUserRole is not the same object as the one you are looking for commentUser.Roles.
Your context object will create a new object when you select from it and populate your Roles property with a collection of new Roles. If your context is not tracking the objects in order to return the same object when a second copy is requested then it will be a different object even though all the properties may be the same. Hence the failure of Contains
Your Any clause is explicitly checking the Name property which is why it works
Try making Role implement IEquatable<Role>
public class Role : IEquatable<Role> {
public bool Equals(Role compare) {
return compare != null && this.Name == compare.Name;
}
}
Whilst MSDN shows you only need this for a List<T> you may actually need to override Equals and GetHashCode to make this work
in which case:
public class Role : IEquatable<Role> {
public bool Equals(Role compare) {
return compare != null && this.Name == compare.Name;
}
public override bool Equals(object compare) {
return this.Equals(compare as Role); // this will call the above equals method
}
public override int GetHashCode() {
return this.Name == null ? 0 : this.Name.GetHashCode();
}
}

Optimal way to ensure uniqueness of values in datastructure?

I am required to key a dictionary based on values of a datastructure. I'm wondering what would be the optimal way to create this key?
The datastructure has 3 values: two strings and a datetime. The three of these values combined represents a "unique" key for my dictionary.
public class RouteIdentity
{
public string RouteId {get;set;}
public string RegionId {get;set;}
public DateTime RouteDate {get;set;}
}
One solution that comes to mind is to add a property to RouteIdentity (called Key perhaps?) that returns some representation of the 3 unique values. The type of Key would be the type of the key value of the dictionary. Key could be a string value that simply concatenates the various properties, but this seems terribly inefficient. I suppose if there were a way to implement a fast hashing function to return a different type that might also work.
Another possibility to is override the Equals operator for RouteIdentity. I'm thinking this might be a better approach, but I'm unsure of how to override the GetHashCode() function for such a purpose.
Can anyone shed some light on what the optimal approach would be for this case? If you feel that it would be best to use operator overloading, could you please provide some guidance as to how to implement it properly?
Thanks in advance.
Implement Equals() and GetHashCode(), ..
public class RouteIdentity
{
public string RouteId { get; set; }
public string RegionId { get; set; }
public DateTime RouteDate { get; set; }
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj.GetType() != typeof(RouteIdentity))
{
return false;
}
RouteIdentity other = (RouteIdentity) obj;
return Equals(other.RouteId, RouteId) &&
Equals(other.RegionId, RegionId) &&
other.RouteDate.Equals(RouteDate);
}
public override int GetHashCode()
{
unchecked
{
int result = (RouteId != null ? RouteId.GetHashCode() : 0);
result = (result * 397) ^ (RegionId != null ? RegionId.GetHashCode() : 0);
result = (result * 397) ^ RouteDate.GetHashCode();
return result;
}
}
}
... and use new Dictionary<RouteIdentity, TValue>(), which internally will instantiate EqualityComparer<RouteIdentity>.Default, which uses these 2 methods to compare your RouteIdentity instances.
Implement IComparable for RouteIdentity and use HashSet<RouteIdentity>.

Categories