I would like to have expression classes that compare two objects and pass the below test.
public abstract class ComparisonExpression
{
public bool Evaluate(IComparable left, object right)
{
if (left == null && right == null)
return true;
if (left == null || right == null)
return false;
return GetResult(left.CompareTo(right));
}
protected abstract bool GetResult(int comparisonResult);
}
public class AreEqualExpression : ComparisonExpression
{
protected override bool GetResult(int comparisonResult)
{
return comparisonResult == 0;
}
}
// TEST
const int i = 123;
const long l = 123L;
const string s = "123";
Assert.IsTrue(new AreEqualExpression().Evaluate(i, l));
Assert.IsFalse(new AreEqualExpression().Evaluate(i, s));
Assert.IsFalse(new AreEqualExpression().Evaluate(l, s));
It seems like IComparable implementation expects the given type matches the current type. I am having an exception like "Object must be of type Int32.".
I thought returning false if types are not equal. It prevents the exception but it brakes the behavior that i want.
Also I thought about a type conversion but this time string and int comparison will return true, which i do not want.
Any suggestions?
If you define this helper class
public static class NumericalHelper {
public static double AsDouble(this object value, out bool success) {
if (value is sbyte || value is byte || value is short || value is ushort || value is int || value is uint || value is long || value is decimal || value is ulong || value is float || value is double || value.GetType().IsEnum) {
success = true;
return Convert.ToDouble(value);
}
success = false;
return 0;
}
}
you can do the comparison like this:
public bool Evaluate(IComparable left, object right) {
if (left == null && right == null)
return true;
if (left == null || right == null)
return false;
bool isNumerical;
double leftValue = left.AsDouble(out isNumerical);
double rightValue = isNumerical ? right.AsDouble(out isNumerical) : 0;
if (isNumerical)
return GetResult(Comparer<Double>.Default.Compare(leftValue, rightValue));
else {
if (left.GetType() == right.GetType())
return GetResult(left.CompareTo(right));
else
return false;
}
But beware that equality is usully compared with the Equals method or with the IEquatable interface, which isn't considered at all in your example. Here is more information about implementing equality. This seems to be an issue to me this doesn't fit well in use current class design.
Related
I have many objects in which I want to check that lists of those objects are equal.
For each object I have defined an EqualityComparer:
public class BaseAssociatedEntity : BasePage
{
protected IWebElement EntityElement;
protected virtual IWebElement EntityLink => EntityElement.FindElement(By.TagName("a"));
public string EntityName => EntityLink.Text;
public BaseAssociatedEntity(IWebElement entityElement, IWebDriver driver, string username, string password)
: base(driver, driver.Url, username, password, TimeoutInSecondsConstants.Three)
{
EntityElement = entityElement;
}
public bool Equals(BaseAssociatedEntity that)
{
return EntityName == that.EntityName;
}
}
public class BaseAssociatedEntityEqual : EqualityComparer<BaseAssociatedEntity>
{
public override bool Equals(BaseAssociatedEntity x, BaseAssociatedEntity y)
{
if (ReferenceEquals(x, y)) return true;
if (x is null || y is null) return false;
return x.Equals(y);
}
public override int GetHashCode(BaseAssociatedEntity obj) => obj.GetHashCode();
}
I then want to call the following method to check that 2 lists of type BaseAssociatedEntity are SequenceEqual():
protected bool BothNullOrEqual(List<BaseAssociatedEntity> left, List<BaseAssociatedEntity> right)
{
if (left == null && right == null) return true;
if (left != null && right != null) return left.SequenceEqual(right, new BaseAssociatedEntityEqual());
return false;
}
But I end up writing this BothNullOrEqual method for every single object that I have:
protected bool BothNullOrEqual(List<NotificationGroupAssociatedEntity> left,
List<NotificationGroupAssociatedEntity> right)
{
if (left == null && right == null) return true;
if (left != null && right != null) return left.SequenceEqual(right, new NotificationGroupAssociatedEntityEqual());
return false;
}
And so on.. How can I make this method generic so that it works for all types, using the EqualityComparer that I have specifically defined?
Just make the method generic in both the data type and the comparer type:
public static bool BothNullOrEqual<TData, TEqualityComparer>
(IEnumerable<TData> left, IEnumerable<TData> right)
where TEqualityComparer : IEqualityComparer<TData>, new()
{
if (left is null && right is null)
{
return true;
}
if (left is null || right is null)
{
return false;
}
return left.SequenceEqual(right, new TEqualityComparer());
}
An alternative single expression version:
public static bool BothNullOrEqual<TData, TEqualityComparer>
(IEnumerable<TData> left, IEnumerable<TData> right)
where TEqualityComparer : IEqualityComparer<TData>, new() =>
left is null
? right is null
: right is object && left.SequenceEqual(right, new TEqualityComparer());
So I created a class like this:
public class ClassName
{
public int ID;
public String n_1 {get; set; }
public String n_2 {get; set; }
// ....
public String n_x {get; set; }
}
Later in my code I compare 2 ClassName objects:
ClassName Item_1 /*...*/ ;
ClassName Item_2 /*...*/ ;
Like this:
if (Item_1 != Item_2 && Item_1.n_a == Item_2.n_a)
{
//do something
}
Now my Problem is that Item_1.ID and Item_2.ID should be ignored. Is there a easy way to do this? The only solution I came up with is a no brainer of like
if ( (Item_1.n_1 != Item_2.n_1 || Item_1.n_2 != Item_2.n_2 || /* ... */ ) && Item_1.n_a == Item_2.n_a)
Override the object.Equals method in your class and specify how you want it to be compared.
Then use !Item1.Equals(Item2) instead of !=
https://learn.microsoft.com/en-us/dotnet/api/system.object.equals?view=netframework-4.7.2
The easy way is what you are actually doing , just create a method like this
public bool Method(ClassName Item_1,ClassName Item2)
{
bool check=false;
if ( (Item_1.n_1 != Item_2.n_1 || Item_1.n_2 != Item_2.n_2 || /* ... */ ) && Item_1.n_a == Item_2.n_a)
check=true
return check
}
To perform something like Item_1 != Item_2 or Item_1 == Item_2 and get all properties (or the ones you want) to be compared, you need to implement your own Equals method and ==, != operators overloads.
== operator, from MSDN:
For reference types other than string, == returns true if its two operands refer to the same object.
For that you can start with some straightforward solution, to implement IEquatable<T> interface, fill your comparing logic inside Equals method and overload == and != operators internally calling your type-safe Equals method:
public class ClassName : IEquatable<ClassName>
{
public int ID;
public String n_1 { get; set; }
public String n_2 { get; set; }
// ....
public String n_x { get; set; }
public static bool operator ==(ClassName obj1, ClassName obj2)
{
if (((object)obj1) == null || ((object)obj2) == null)
return Equals(obj1, obj2);
return obj1.Equals(obj2);
}
public static bool operator != (ClassName obj1, ClassName obj2)
{
if (((object)obj1) == null || ((object)obj2) == null)
return !Equals(obj1, obj2);
return !obj1.Equals(obj2);
}
public bool Equals(ClassName obj)
{
if (obj == null) return false;
return (n_1 == obj.n_1) && (n_2 == obj.n_2) && (n_x == obj.n_x); //you can ignore ID here
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
ClassName classNameObj = obj as ClassName;
if (classNameObj == null)
return false;
else
return Equals(classNameObj);
}
public override int GetHashCode()
{
//This code was generated by VS ide, you can write your own hashing logic
var hashCode = 1032198799;
hashCode = hashCode * -1521134295 + ID.GetHashCode();
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(n_1);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(n_2);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(n_x);
return hashCode;
}
}
Then you can check in your if:
if (Item_1 != Item_2)
{
//Do Something
}
For Equals method you should stick to Guidelines for Overriding Equals() and Operator == .
References: == Operator, != Operator, IEquatable Interface, IEquatable.Equals(T) Method, Guidelines for Overriding Equals() and Operator ==
Recently I decided to play around with overriding the Equals method. I used mainly the MSDN guideline (the updated one I believe). So my implementation end up like this:
public class EqualityCheck
{
public int Age { get; set; }
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
public override bool Equals(object obj)
{
if (null == obj)
{
return false;
}
//If obj is on of the expected type return false
EqualityCheck ec = obj as EqualityCheck;
if (null == ec)
{
return false;
}
//return true if the fields match. This is the place where we can decide what combination should be unique
return (Age == ec.Age) && (Name == ec.Name) && (DateOfBirth == ec.DateOfBirth);
}
public bool Equals(EqualityCheck ec)
{
// If parameter is null return false:
if (ec == null)
{
return false;
}
// Return true if the fields match:
return (Age == ec.Age) && (Name == ec.Name);
}
//How to implement GetHashCode for complex object?
}
And also one child class:
public class EqualityCheckChild : EqualityCheck
{
public int Height { get; set; }
public override bool Equals(System.Object obj)
{
// If parameter cannot be cast to ThreeDPoint return false:
EqualityCheckChild ec1 = obj as EqualityCheckChild;
if (ec1 == null)
{
return false;
}
// Return true if the fields match:
return base.Equals(obj) && Height == ec1.Height;
}
public bool Equals(EqualityCheckChild ec1)
{
// Return true if the fields match:
return base.Equals((EqualityCheck)ec1) && Height == ec1.Height;
}
public static bool operator ==(EqualityCheckChild a, EqualityCheckChild b)
{
if (Equals(a, b))
{
return true;
}
if (a == null || b == null)
{
return false;
}
return a.DateOfBirth == b.DateOfBirth && a.Name == b.Name;
}
public static bool operator !=(EqualityCheckChild a, EqualityCheckChild b)
{
return !(a == b);
}
}
I think this is what MSDN shows as implementation, just with removed casts.
I am using VS2015 and .NET 4.5.2. VS marked the casting as redundant, that's when I look up what's the issue with this. I've read about the possible infinity loop and just out of curiosity I decided to recreate it with the code above. However I can't reproduce it.
Since this is not copy-paste code. At least not literally. I wrote it by hand trying to understand what I am doing so I guess it's possible to have some mismatch with the original code which leads to this. But still my question remains - how to reproduce the problem which is also mentioned in the MSDN article?
Attempt to compare an instance of EqualityCheckChild to null.
EqualityCheckChild foo = new EqualityCheckChild();
Console.WriteLine(foo == null);
This code snippet will cause a StackOverflowException, because in the operator== method, if (a == null || b == null) calls itself.
I have a Venue class, and a Coordinate class like so:
class Venue
{
string Name;
Coordinate coordinate;
}
class Coordinate
{
double latitute;
double longitude;
}
Now, I want to be able to select a venue based on a coordinate as follows:
List<Venue> venues = GetAllVenues();
var myVenue = venues.FirstOrDefault(venue=>venue.coordinate == myCoordinate);
I have an IEqualityComparer implementation, but the lambda expression does not have an overload which takes the IEqualityComparer as a parameter.
How do I use my equality comparer in a lambda expression?
EDIT:
My equality comparer looks like this:
class CoordinatesEqualityComparer:IEqualityComparer<Coordinate>
{
public bool Equals(Coordinate x, Coordinate y)
{
return x.RowIndex == y.RowIndex && x.ColumnIndex == y.ColumnIndex;
}
public int GetHashCode(Coordinate obj)
{
return obj.GetHashCode();
}
}
When I do a Union() operation, like so, it does not work correctly, even though coordinates in both lists are same.
List<Coordinates> coordinates;
CoordinatesEqualityComparer comparer;
coordinates.Union(someOtherListOfCoordinates, comparer);
However, when I do a union with itself, it works. What am I doing wrong? Does it have something to do with the GetHashCode() implementation?
Edit 2:
Fixing the GetHashCode() method seems to do the trick.
public int GetHashCode(Coordinates obj)
{
// Warning:Hack. Use two prime numbers to generate a hash based on two properties.
return obj.RowIndex.GetHashCode() * 7 + obj.ColumnIndex.GetHashCode() * 13 ;
}
Have you tried:
var ec = new YourEqualityComparer();
var myVenue = venues.FirstOrDefault(venue =>
ec.Equals(venue.coordinate, myCoordinate));
Of course, another approach would be to define the == operator for your Coordinate class and then you wouldn't need an IEqualityComparer:
class Coordinate
{
double latitude;
double longitude;
public override bool Equals(object obj)
{
return Object.ReferenceEquals(this, obj)) ||
this == (other as Coordinate);
}
public static bool operator ==(Coordinate l, Coordinate r)
{
return ((object)l == null && (object)r == null) ||
((object)l != null && (object)r != null) &&
// equality check including epsilons, edge cases, etc.
}
public static bool operator !=(Coordinate l, Coordinate r)
{
return !(l == r);
}
}
I would implement IEquatable<Coordinate>, override Equals(object), override GetHashCode(), and == != operators like this:
public class Coordinate : IEquatable<Coordinate>
{
public double Latitide { get; set; }
public double Longitude { get; set; }
public bool Equals(Coordinate other)
{
if (other == null)
{
return false;
}
else
{
return this.Latitide == other.Latitide && this.Longitude == other.Longitude;
}
}
public override bool Equals(object obj)
{
return this.Equals(obj as Coordinate);
}
public override int GetHashCode()
{
return this.Latitide.GetHashCode() ^ this.Longitude.GetHashCode();
}
public static bool operator ==(Coordinate value1, Coordinate value2)
{
if (!Object.ReferenceEquals(value1, null) && Object.ReferenceEquals(value2, null))
{
return false;
}
else if (Object.ReferenceEquals(value1, null) && !Object.ReferenceEquals(value2, null))
{
return false;
}
else if (Object.ReferenceEquals(value1, null) && Object.ReferenceEquals(value2, null))
{
return true;
}
else
{
return value1.Latitide == value2.Latitide && value1.Longitude == value2.Longitude;
}
}
public static bool operator !=(Coordinate value1, Coordinate value2)
{
return !(value1 == value2);
}
}
Does exist to make short code which i can replace if() argument for example:
int x = 1; // x can be 1,2,3 etc.
if(x==1 || x==3 || x==12)
{
//do something..
}
I don't want to repeat x==1, x==3, etc. just compare numbers to x.
You can have the possible numbers in an array and then compare it like:
int x = 1;
int[] compareArray = new[] { 1, 3, 12, 33 };
if (compareArray.Contains(x))
{
//condition met
}
Or you can use Enumerable.Any like:
if (compareArray.Any(r=> r == x))
You can do it fairly succinctly with a switch statement:
int x= 1;
switch (x)
{
case 1:
case 2:
case 3:
case 4:
Console.WriteLine("x is either 1, 2, 3, or 4");
break
default:
Console.WriteLine("x is not 1, 2, 3, or 4");
break;
}
Late to the party, and I would definitely, definitely suggest that one use a simple solution such as Habib's answer (and even simplifying it with extension methods). That said, I was curious if it were possible to achieve a minimal syntax along the lines of what Tagon may have been looking for. That is, would it be possible to have something like:
int x = 1;
if(x == 1 || 3 || 12 || 33)
{
}
I suspect there is unneeded verbosity with some of the operators I used here (and certainly some best-practices violations), but something like this is possible with operator overloads. The resulting syntax is:
IntComparer x = 1; //notice the usage of IntComparer instead of int
if(x == 1 || 3 || 12 || 33)
{
}
First I have a kind of "entry point" into the comparison:
public class IntComparer
{
public int Value { get; private set; }
public IntComparer(int value)
{
this.Value = value;
}
public static implicit operator IntComparer(int value)
{
return new IntComparer(value);
}
public static BoolComparer operator ==(IntComparer comparer, int value)
{
return new BoolComparer(comparer.Value, comparer.Value == value);
}
public static BoolComparer operator !=(IntComparer comparer, int value)
{
return new BoolComparer(comparer.Value, comparer.Value != value);
}
}
This satisfies the initial x == 1 check. From here on, it switches to a new type BoolComparer:
public class BoolComparer
{
public int Value { get; private set; }
public bool IsSatisfied { get; private set; }
public BoolComparer(int value, bool isSatisfied)
{
this.Value = value;
this.IsSatisfied = isSatisfied;
}
public static bool operator true(BoolComparer comparer)
{
return comparer.IsSatisfied;
}
public static bool operator false(BoolComparer comparer)
{
return !comparer.IsSatisfied;
}
public static implicit operator bool(BoolComparer comparer)
{
return comparer.IsSatisfied;
}
public static BoolComparer operator |(BoolComparer comparer, BoolComparer value)
{
return new BoolComparer(comparer.Value, comparer.Value == value.Value);
}
public static implicit operator BoolComparer(int value)
{
return new BoolComparer(value, false);
}
}
This satisfies the subsequent || 3 || 12 || 33 checks and the final true/false evaluation for the if statement.
These two classes working in conjunction makes the syntax tomfoolery work. It's terrible. Do not use it. Also, it doesn't work for for the negative: if (x != 1 || 3 || 12 || 33). That might be a simple fix, but I don't want to dive into that at the moment)
Some working checks:
IntComparer x = 1;
bool check1 = x == 1 || 3 || 12 || 33; //true
bool check2 = x == 2 || 3 || 12 || 33; //false
bool check3 = x == 5 || 1 || Int32.MaxValue; //true
bool check4 = x == -1; //false