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.
Related
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.
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.
What would be the best (most elegant or performing) way of overloading the equality operator on a class containing only string attributes?
Example:
class MagicClass
{
public string FirstAttribute { get; set; }
public string SecondAttribute { get; set; }
public string ThirdAttribute { get; set; }
public string FourthAttribute { get; set; }
public string FifthAttribute { get; set; }
}
I know how to overload the operator itself, however, I am wondering about the following points:
Is there a way to elegantly compare such two objects (e.g. without having to write an if statement containing mutual comparisons of all the attributes
What would be a good implementation of the GetHashCode() method in such case
How about something like this, Just create array of all properties and a loop.
internal class MagicClass
{
public string FirstAttribute { get; set; }
public string SecondAttribute { get; set; }
public string ThirdAttribute { get; set; }
public string FourthAttribute { get; set; }
public string FifthAttribute { get; set; }
private string[] AllProperties//Array of all properties
{
get
{
return new[]
{
FirstAttribute,
SecondAttribute,
ThirdAttribute,
FourthAttribute,
FifthAttribute
};
}
}
protected bool Equals(MagicClass other)
{
var thisProps = this.AllProperties;
var otherProps = other.AllProperties;
return thisProps.SequenceEqual(otherProps);
}
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((MagicClass) obj);
}
public override int GetHashCode()
{
unchecked
{
var thisProps = this.AllProperties;
int hashCode = 0;
foreach (var prop in thisProps)
{
hashCode = (hashCode * 397) ^ (prop != null ? prop.GetHashCode() : 0);
}
return hashCode;
}
}
}
Then you can call Equals method inside your operator overload. If you're lazy to create AllProperties array you can use Reflection but IMO reflection is overkill here.
Not saying this is the 'best' or the most elegant solution, but I'd have the tendency to use an array and an index initializer, using an enumeration, so I could reuse get and set logic and in this case reset a hash code for a quick first comparison.
The advantage of the enumeration is, that you don't have to recheck your compare logic when an attribute is added, and you can prevent the overhead of resorting to reflection.
class MagicClass
{
string[] Values = new string[Enum.GetValues(typeof(MagicClassValues)).Length];
public string this[MagicClassValues Value] //and/or a GetValue/SetValue construction
{
get
{
return Values[(int)Value];
}
set
{
Values[(int)Value] = value;
hash = null;
}
}
int? hash; //buffered for optimal dictionary performance and == comparisson
public override int GetHashCode()
{
if (hash == null)
unchecked
{
hash = Values.Sum(s => s.GetHashCode());
}
return hash.Value;
}
public static bool operator ==(MagicClass v1, MagicClass v2) //used == operator, in compliance to the question, but this would be better for 'Equals'
{
if(ReferenceEquals(v1,v2))return true;
if(ReferenceEquals(v1,null) || ReferenceEquals(v2,null) || v1.GetHashCode() != v2.GetHashCode())return false;
return v1.Values.SequenceEqual(v2.Values);
}
public static bool operator !=(MagicClass v1, MagicClass v2)
{
return !(v1 == v2);
}
//optional, use hard named properties as well
public string FirstAttribute { get { return this[MagicClassValues.FirstAttribute]; } set { this[MagicClassValues.FirstAttribute] = value; } }
}
public enum MagicClassValues
{
FirstAttribute,
SecondAttribute,
//etc
}
let me start right away with the code:
class Item {
public int highestBuyOffer;
public int lowestSellOffer;
[...]
}
I would like to prevent people using this class from accidently assigning a buy offer value to a sell offer value and the other way round (like someBuyOffer = someSellOffer). That's why I want to create my own types:
class Item {
public BuyOffer highestBuyOffer;
public SellOffer lowestSellOffer;
[...]
}
Creating a struct for it seems overkill, as these both of values should behave exactly like an int.
The using directive is not what I want because:
It is only valid for one file
It does not count as a type, it's just a synonym
I made this class to cover identical needs:
public class NamedInt : IComparable<int>, IEquatable<int>
{
internal int Value { get; }
protected NamedInt() { }
protected NamedInt(int val) { Value = val; }
protected NamedInt(string val) { Value = Convert.ToInt32(val); }
public static implicit operator int (NamedInt val) { return val.Value; }
public static bool operator ==(NamedInt a, int b) { return a?.Value == b; }
public static bool operator ==(NamedInt a, NamedInt b) { return a?.Value == b?.Value; }
public static bool operator !=(NamedInt a, int b) { return !(a==b); }
public static bool operator !=(NamedInt a, NamedInt b) { return !(a==b); }
public bool Equals(int other) { return Equals(new NamedInt(other)); }
public override bool Equals(object other) {
if ((other.GetType() != GetType() && other.GetType() != typeof(string))) return false;
return Equals(new NamedInt(other.ToString()));
}
private bool Equals(NamedInt other) {
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(Value, other.Value);
}
public int CompareTo(int other) { return Value - other; }
public int CompareTo(NamedInt other) { return Value - other.Value; }
public override int GetHashCode() { return Value.GetHashCode(); }
public override string ToString() { return Value.ToString(); }
}
And to consume it in your case:
public class BuyOffer: NamedInt {
public BuyOffer(int value) : base(value) { }
public static implicit operator BuyOffer(int value) { return new BuyOffer(value); }
}
public class SellOffer: NamedInt {
public SellOffer(int value) : base(value) { }
public static implicit operator SellOffer(int value) { return new SellOffer(value); }
}
If you need to be able to serialize it (Newtonsoft.Json), let me know and I'll add the code.
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));
}