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.
Related
I have class A:
public class A : IEquatable<A>
{
public B Owner { get; set; }
public override bool Equals(object obj)
{
return Equals(obj as A);
}
public bool Equals([AllowNull] A other)
{
return other is A a &&
EqualityComparer<B>.Default.Equals(Owner, a.Owner);
}
}
And I have a class B:
public class B : IEquatable<B>
{
public List<A> Children { get; set; } = new List<A>();
public override bool Equals(object obj)
{
return Equals(obj as B);
}
public bool Equals([AllowNull] B other)
{
return other is B b &&
EqualityComparer<List<A>>.Default.Equals(Children, b.Children);
}
}
The problem I am having is making Equals() methods of the above classes work. The Equals() methods in the example are generated by VS Code, but always return false in case of class B.
I also tried using LINQ expressions (such as SequenceEqual method), but it always results in Stack Overflow (because of circular dependency?).
As a side note, I used .NET Core 3.0 to run this.
So, I managed to find the answer to my question. I just implemented my own custom IEqualityComparer. (in the example below I added public Guid ID property to both classes to do proper GetHashCode()).
public class BComparer : IEqualityComparer<B>
{
public bool Equals([AllowNull] B x, [AllowNull] B y)
{
if (x is null || y is null) {return false;}
if (x.ID == y.ID) {
return x.Children.SequenceEqual(y.Children);
} else {
return false;
}
}
public int GetHashCode([DisallowNull] B obj)
{
return obj.ID.ToString().GetHashCode();
}
}
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));
}
How can I make a class like this:
public class Foo
{
private string m_value;
public string Value { get { return m_value; } set { m_value = value; } }
public Foo()
{
m_value = "";
}
}
be directly comparable with a string without needing to access it's .Value property so that I can do:
Foo m_foo = new Foo();
m_foo = "someString";
if(m_foo == "someString")
{
Console.Writeline("It Worked!");
}
I'm not sure if this is even possible.
You can overload the == operator (the compiler also requires you overload '!=', 'Equals', and 'GetHashCode')
public class Foo
{
private string m_value;
public string Value { get { return m_value; } set { m_value = value; } }
public Foo()
{
m_value = "";
}
public override bool Equals(object obj)
{
return this.Value == obj.ToString();
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
public static bool operator ==(Foo a, string b)
{
return a.Value == b;
}
public static bool operator !=(Foo a, string b)
{
return a.Value != b;
}
}
I'm not sure if all of that effort is worth it though, when you could just write:
if(m_foo.Value == "someString")
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.
foreach (Ant ant in state.MyAnts)
{
if (m_foodTasks.ContainsKey(ant))
{
...
By debugging I can see that there's an ant inside m_foodTasks with the exactly same values. So I will assume it compares the reference address..(am I right?)
How do I make it compare by value?
Edit after reading answers:
Too many info...
I will take a while to study those, but here is what the class ant already have ( I cant say what all those stuff is):
public class Location : IEquatable<Location> {
/// <summary>
/// Gets the row of this location.
/// </summary>
public int Row { get; private set; }
/// <summary>
/// Gets the column of this location.
/// </summary>
public int Col { get; private set; }
public Location (int row, int col) {
this.Row = row;
this.Col = col;
}
public override bool Equals (object obj) {
if (ReferenceEquals (null, obj))
return false;
if (ReferenceEquals (this, obj))
return true;
if (obj.GetType() != typeof (Location))
return false;
return Equals ((Location) obj);
}
public bool Equals (Location other) {
if (ReferenceEquals (null, other))
return false;
if (ReferenceEquals (this, other))
return true;
return other.Row == this.Row && other.Col == this.Col;
}
public override int GetHashCode()
{
unchecked {
return (this.Row * 397) ^ this.Col;
}
}
}
public class TeamLocation : Location, IEquatable<TeamLocation> {
/// <summary>
/// Gets the team of this ant.
/// </summary>
public int Team { get; private set; }
public TeamLocation (int row, int col, int team) : base (row, col) {
this.Team = team;
}
public bool Equals(TeamLocation other) {
return base.Equals (other) && other.Team == Team;
}
public override int GetHashCode()
{
unchecked {
int result = this.Col;
result = (result * 397) ^ this.Row;
result = (result * 397) ^ this.Team;
return result;
}
}
}
public class Ant : TeamLocation, IEquatable<Ant> {
public Ant (int row, int col, int team) : base (row, col, team) {
}
public bool Equals (Ant other) {
return base.Equals (other);
}
}
The GetHashCode() method needs to be overridden when checking for equality.
public class Ant : IEquatable<Ant>
{
private string _someField;
public Ant(string someField)
{
this._someField = someField;
}
#region Equality members
public bool Equals(Ant other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return string.Equals(_someField, other._someField);
}
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((Ant) obj);
}
public override int GetHashCode()
{
return (_someField != null ? _someField.GetHashCode() : 0);
}
#endregion
}
You can optionally create a IEqualityComparer class that does the comparing for you.
private sealed class SomeFieldEqualityComparer : IEqualityComparer<Ant>
{
public bool Equals(Ant x, Ant y)
{
if (ReferenceEquals(x, y))
{
return true;
}
if (ReferenceEquals(x, null))
{
return false;
}
if (ReferenceEquals(y, null))
{
return false;
}
if (x.GetType() != y.GetType())
{
return false;
}
return string.Equals(x._someField, y._someField);
}
public int GetHashCode(Ant obj)
{
return (obj._someField != null ? obj._someField.GetHashCode() : 0);
}
}
You need to implement GetHash and Equals correctly to make class behave as "compare values" in searches/dictionaries instead of "compare reference".
Another (potentially better option) is to provide IEqualityComparer when creating dictionary.
Sample can be found in the article, here is compressed version:
Dictionary<Box, String> boxes =
new Dictionary<Box, string>(new BoxEqualityComparer());
class BoxEqualityComparer : IEqualityComparer<Box>
{
public bool Equals(Box b1, Box b2)
{
return b1.Height == b2.Height;
}
public int GetHashCode(Box bx)
{
return bx.Height.GetHashCode();
}
}