Hash set - unique items not added - c#

I have a class that contains the following:
HashSet<CookieSetItem> _set = new HashSet<CookieSetItem>();
public IEnumerable<CookieSetItem> Set
{
get { return _set; }
}
public void Add(int id)
{
id.ThrowDefault("id");
var item = new CookieSetItem(id);
if (_set.Add(item))
{
// this only happens for the first call
base.Add();
}
}
When I call the add method multiple times, say with ID's 1,2,3 etc, only the first item is added.
Obviously I'm confused as a new CookieSetItem is being created each time with a unique element (the ID), so why is it not being added?.
For completeness, here's the cookie set class:
public sealed class CookieSetItem
{
readonly DateTime _added;
readonly int _id;
public DateTime Added
{
get { return _added; }
}
public int ID
{
get { return _id; }
}
public CookieSetItem(int id)
: this(id, DateTime.Now)
{
}
public CookieSetItem(int id, DateTime added)
{
id.ThrowDefault("id");
added.ThrowDefault("added");
_id = id;
_added = added;
}
}

Got to the bottom of it - more than one error, which clouded the overall view.
Firstly I updated my class with IEquatable, which fixed the adding problem. Secondly, I found that the end result which was to update a cookie with a string version of the hashset also failed due to the fact that it was not encrypted. Here's the amended class that fixed the original problem.
public sealed class DatedSet : IEquatable<DatedSet>
{
readonly DateTime _added;
readonly int _id;
public DateTime Added
{
get { return _added; }
}
public int ID
{
get { return _id; }
}
public DatedSet(int id)
: this(id, DateTime.Now)
{
}
public DatedSet(int id, DateTime added)
{
id.ThrowDefault("id");
added.ThrowDefault("added");
_id = id;
_added = added;
}
public bool Equals(DatedSet other)
{
if (other == null) return false;
return this.ID == other.ID;
}
public override bool Equals(Object obj)
{
if (obj == null) return false;
var ds = obj as DatedSet;
return ds == null ? false : Equals(ds);
}
public override int GetHashCode()
{
return ID.GetHashCode();
}
}
Thanks for the advice.

Related

Avoid adding duplicate object in a list before adding a custom object to list?

I red a few articles on internet but all value to me, I couldn't understand how can I avoid adding a duplicate object to a list, I tried something like this.
I actually have created a class which overrides GetHashCode and Equal method.
Now I want to form a collection of non duplicate object list.
public class FlightInfo
{
public string Origin { get; set; }
public string DepartureTime { get; set; }
public string Destination { get; set; }
public string DestinationTime { get; set; }
public string Price { get; set; }
public override bool Equals(object obj)
{
var other = obj as FlightInfo;
if (other == null)
return false;
if (Origin != other.Origin || DepartureTime != other.DepartureTime || Destination != other.Destination
|| DestinationTime != other.DestinationTime || Price != other.Price)
return false;
return true;
}
public override int GetHashCode()
{
int hashOrigin = Origin.GetHashCode();
int hashDestination = Destination.GetHashCode();
int hashDepartureTime = DepartureTime.GetHashCode();
int hashDestinationTime = DestinationTime.GetHashCode();
int hashPrice = Price.GetHashCode();
return hashOrigin ^ hashDestination ^ hashDepartureTime ^ hashDestinationTime ^ hashPrice;
}
}
I also tried one article by Eric
https://blogs.msdn.microsoft.com/ericlippert/2011/02/28/guidelines-and-rules-for-gethashcode/
but this article has
private List<T>[] buckets = new List<T>[100];
insead of private List<T>() buckets = new List<T>()
but I want to return a list with no fix size.
Since you already implemented the Equals and GetHashCode methods you can have your own custom list of FlightInfo that will make use of those methods:
public class FlightInfoList : IList<FlightInfo>
{
private readonly List<FlightInfo> _flightInfos = new List<FlightInfo>();
public IEnumerator<FlightInfo> GetEnumerator()
{
return _flightInfos.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Add(FlightInfo item)
{
if (_flightInfos.Any(flightInfo => flightInfo.Equals(item)))
{
throw new Exception("Cannot add duplicated values!");
}
_flightInfos.Add(item);
}
public void Clear()
{
_flightInfos.Clear();
}
public bool Contains(FlightInfo item)
{
return _flightInfos.Contains(item);
}
public void CopyTo(FlightInfo[] array, int arrayIndex)
{
_flightInfos.CopyTo(array, arrayIndex);
}
public bool Remove(FlightInfo item)
{
return _flightInfos.Remove(item);
}
public int Count => _flightInfos.Count;
public bool IsReadOnly => false;
public int IndexOf(FlightInfo item)
{
return _flightInfos.IndexOf(item);
}
public void Insert(int index, FlightInfo item)
{
_flightInfos.Insert(index, item);
}
public void RemoveAt(int index)
{
_flightInfos.RemoveAt(index);
}
public FlightInfo this[int index]
{
get => _flightInfos[index];
set => _flightInfos[index] = value;
}
}
Notice that in the Add method I'm checking if there's a duplicated. Another way to solve this is to use a dictionary.

C# Can't access inherited protected variable from Generic class

First time poster...
New to C# and Generics and I have been experimenting by creating a simple series of Object Tables for read-only data entries.
On my Generic Insert routine I increment a static Id variable to ensure it is always unique. To try and prevent it being modified I set it to protected but the Generic class which then throws a compile error stating that Id can't be accessed.
I am struggling to find out why exactly as I thought "where T : DBEntity" would allow this.
Thanks in advance:
public class DBEntity
{
public int Id { get; protected set; }
}
public class Table<T> where T : DBEntity
{
static int _id = 0;
private readonly List<T> _set = new List<T>();
public IEnumerable<T> Set() { return _set; }
public void Insert(T item)
{
_id++;
item.Id = _id; //when set to protected it is inaccessible
_set.Add(item);
}
}
You're protecting the ID, so you can't set it. It's honestly as simple as that.
Also doing a generic of Table, and tying the generic to a concrete class buys you nothing. Consider an interface instead.
You could fix your issue as the following:
public interface IDatabaseItem
{
int? Id { get; }
SetID(int value);
}
public class DBEntity : IDatabaseItem
{
public int? Id { get; private set; }
public void SetID(int value)
{
if (Id == null)
{
Id = value;
}
else
{
throw new Exception("Cannot set assigned Id; can only set Id when it is not assgined.");
}
}
}
public class Table<T> where T : IDatabaseItem
{
static int _id = 0;
private readonly List<T> _set = new List<T>();
public IEnumerable<T> Set() { return _set; }
public void Insert(T item)
{
if (item.Id == null)
{
_id++;
item.SetID(_id);
_set.Add(item);
}
else
{
//Handle this case. Something else set the ID, yet you're trying to insert it. This would, with your code, imply a bug.
}
}
}

Why i can't modify these values in an object?

I have a problem, if i want to edit a value into an object and call it again, i get the old value instead of the new one.
Here is my Function to get the Class:
public static Clan GetClanByID(int Index)
{
foreach (Clan Clan in Clans)
{
if (Clan.ID == Index)
{
return Clan;
}
}
return new Clan()
{
ID = -1,
Name = "NULL",
IconID = -1
};
}
Here is my function to update the Value:
public static void ChangeAnnouncement(int ClanID, int Mode, string Text)
{
if (ClanID != -1)
{
Clan Clan = GetClanByID(ClanID);
if (Mode == 0)
{
Clan.Description = Text;
}
else if (Mode == 1)
{
Clan.News = Text;
}
}
}
And here is the class:
public class Clan
{
public int ID;
public int LeaderID;
public int Extension;
public int CreationDate;
public long EXP;
public long IconID;
public string Name;
public string News;
public string Description;
public List<ClanWars> ClanWars;
public List<ClanUsers> Users;
public List<ClanCoMasters> CoMasters;
public List<ClanPendingUsers> PendingUsers;
public LeaderInformations LeaderInformations;
}
Example: The old value was "123", than i call the function ChangeAnnouncement, and set the value to "1234567890", after that i get the value using GetClanByID(ID).Description, but i will still get the old value ("123")
I Hope you can help me.
You're returning a new Clan every time. You forgot adding the newly created clan to Clans.

HashSet<T> quickly checking if identity exists by T.identity

I have an entity called Feature which contains a value identity called FeatureIdentity.
I have a list of these entities, and i want to quickly determine if the identity already exists.
The kicker is i need to be able to compare by the FeatureIdentity and not be the Feature, the Contains procedure on lists is checking against a provided T parameter.
So I am currently doing the code:
public class SomeClass
{
HashSet<Feature> features = new HashSet<Feature>();
public void SetRequirement(FeatureIdentity feature, FeatureIdentity requires)
{
if (ContainsFeature(feature) == false || ContainsFeature(requires) == false)
{
// throw
}
this.requirements.Add(feature, requires);
}
bool ContainsFeature(FeatureIdentity identity)
{
return this.features.Where(x => x.Id.Equals(identity)).Count() > 0;
}
}
Does Linq optimize this, or is this there a correct optimal way of checking if the item exists?
public class Feature
{
public Feature(FeatureIdentity id, string name)
{
this.id = id;
this.name = name;
}
FeatureIdentity id;
string name;
FeatureIdentity Id
{
get { return this.id; }
}
}
public class FeatureIdentity : IEquatable<FeatureIdentity>
{
private readonly string sku;
public FeatureIdentity(string sku)
{
this.sku = sku;
}
public bool Equals(FeatureIdentity other)
{
return this.sku == other.sku;
}
public string Sku
{
get { return this.sku; }
}
public override int GetHashCode()
{
return this.sku.GetHashCode();
}
}
with ctor public HashSet(), HashSet<Feature> is using EqualityComparer<Feature>.Default as default Comparer.
if you use HashSet<Feature>, you should implement IEquatable<Feature> and override GetHashCode.
public class Feature: IEquatable<Feature>
{
public bool Equals(Feature other)
{
return this.id.Equals(other.id);
}
public override int GetHashCode()
{
return this.id.GetHashCode();
}
}
then you can try following workaround which waster a temp object from heap.
bool ContainsFeature(FeatureIdentity identity)
{
return this.features.Contain(new Feature(identity, null));
}

IComparable required to sort column

One of the columns in my DevExpress xtragrid is not sorting, grouping or filtering. Answers to similar questions suggest I need to implement IComparable, but when I did that it no longer displays in the column at all.
public class Flow : System.IComparable<Flow>
{
public Flow(int id, string name, string description)
{
this.ID = id;
this.Name = name;
this.Description = description;
}
public int ID { get; private set; }
public string Name { get; private set; }
public string Description { get; private set; }
public override string ToString()
{
return Name;
}
public override bool Equals(object obj)
{
Flow flow = obj as Flow;
if (flow == null) return false;
return this.ID == flow.ID;
}
public static bool operator ==(Flow flow1, Flow flow2)
{
if (object.ReferenceEquals(null, flow1))
return object.ReferenceEquals(null, flow2);
return flow1.Equals(flow2);
}
public static bool operator !=(Flow flow1, Flow flow2)
{
return !(flow1 == flow2);
}
public override int GetHashCode()
{
return ID;
}
public int CompareTo(Flow other)
{
return this.Name.CompareTo(other.Name);
}
}
What have I done wrong?
UPDATE:
Asked on DevExpress...
The disappearing content was an unrelated issue - a red herring. The column allowed sorting once I had implemented IComparable rather than IComparable<Flow>
public int CompareTo(object obj)
{
if (object.ReferenceEquals(null, obj))
return 1;
Flow flow = obj as Flow;
if (flow == null)
throw new ArgumentException("Object is not of type Flow");
return this.Name.CompareTo(flow.Name);
}
Sourced from MSDN documentation for IComparable.CompareTo Method
It looks like your CompareTo Method is wrong. Try adding the following to the CompareTo() Method and see if it's working:
public int CompareTo(Flow other)
{
// Alphabetic sort if name is equal.
if this.Name == other.Name
{
return this.Name.CompareTo(other.Name);
}
//Default sort.
return other.Name.CompareTo(this.Name);
}
Let me know if it sorted out your problem.

Categories