is there any way to do this on a class and still retain the capability to detect whether a variable of the Type is null ? I can do this for structs, (cause struct can't really be null, with a struct, instead of comparing x with null, you use a separate method caleld IsNull or HasValue... but you can't use that technique with a class cause when the class variable is null, of course, you can't call such an instance method on it !
public class myClass: IEquatable<myClass>
{
public int IntValue { get; set; }
public static bool operator ==(myClass cA, myClass cB)
{ return cA is myClass && cB is myClass && cB.Equals(cA); }
public static bool operator !=(myClass cA, myClass cB)
{ return !(cA == cB); }
public override bool Equals(object o)
{ return o is myClass && this == (myClass)o; }
public override bool Equals(myClass o)
{ return IntValue == o.IntValue; }
}
but when I go:
myClass x;
if (x != null) // this returns false when it should be true
{
//code to execute with x here
}
For what it's worth, the only reason I want this to be a class is because it participates in a simple inheritance relationship. This is an immutable class, and in effect what I am trying to do here is code it so it can behave like an immutable and nullable class, exactly like an immutable, nullable struct behaves (such as Nullable<int> or Nullable<float> etc.
That's why IsNull should be a static method taking a parameter. string.IsNullOrEmpty is a good example. After that, nothing prevents you from making it an extension method.
public class MyClass
{
public static bool IsNull(MyClass other)
{ return ReferenceEquals(other, null); }
public static bool HasValue(MyClass other)
{ return !IsNull(other); }
// other code
}
public static class MyClassExtension
{
public static bool IsNull(this MyClass myClass)
{
return MyClass.IsNull(myClass);
}
}
This will let you do the following without throwing:
MyClass myClass = null;
if(myClass.IsNull())
{
//...
}
If you're not initializing x so it is null, you need to do this
myClass x = new myClass();
if (x != null) {/* is true */}
You can always use:
ReferenceEquals(x, null)
this returns a boolean value showing whether x is null.
You should not do this at all.
You should only do this for an immutable class, if you really must. And then,
You should follow the guidelines
public class myClass: IEquatable<myClass>
{
public static bool operator ==(myClass cA, myClass cB)
{ return (cB == null && cA = null) || (cA is myClass && cB is myClass && cB.Equals(cA)); }
}
Why not just override GetHashCode and Equals?
when you override them both it allows you do do both == and != very easily..
Related
We have GUIDs as identifiers in our systems. As it's easy to mess up and pass the id of one entity into a method that expects the id of another entity (lets say you pass the OrderId to the InvoiceId by mistake because it's all Guids) we created our own types for Guids, so the compiler can easily tell me "hey, don't pass an OrderId here, I expect an InvoiceId".
So basically, we have lots of wrappers around Guid. Those wrappers work well, they are basically copies of the Guid interface delegating all the work to their internally stored Guid.
One thing that I cannot figure out is that Assert.AreEqual(a, b) on two of our custom identifiers will fail. It calls object.Equals(a, b) that in turn calls a == b and that will not call my operator == but instead call something else and return false. It does not for Guid though and I cannot figure out what I missed.
What do I need to implement for my custom types to actually work and return true on object.Equals(a, b) given that it already does on operator ==?
namespace ConsoleApp13
{
using System;
using System.Runtime.InteropServices;
//// Same signature, interfaces and and attributes as
//// https://referencesource.microsoft.com/#mscorlib/system/guid.cs
[StructLayout(LayoutKind.Sequential)]
[Serializable]
[ComVisible(true)]
// not accessible for me: [System.Runtime.Versioning.NonVersionable]
public struct CustomId : IFormattable, IComparable, IComparable<CustomId>, IEquatable<CustomId>
{
public static readonly CustomId Empty = new CustomId();
private readonly Guid internalGuid;
private CustomId(Guid guid)
{
this.internalGuid = guid;
}
public static bool operator ==(CustomId a, CustomId b)
{
return a.internalGuid == b.internalGuid;
}
public static bool operator !=(CustomId a, CustomId b)
{
return !(a.internalGuid == b.internalGuid);
}
public static CustomId NewGuid()
{
return new CustomId(Guid.NewGuid());
}
public static implicit operator Guid(CustomId value)
{
return value.internalGuid;
}
public static explicit operator CustomId(Guid value)
{
return new CustomId(value);
}
public override string ToString()
{
return "[" + this.GetType().Name + ":" + this.internalGuid.ToString("D") + "]";
}
public override int GetHashCode()
{
return this.internalGuid.GetHashCode();
}
public override bool Equals(object obj)
{
return this.internalGuid.Equals(obj);
}
public bool Equals(CustomId other)
{
return this.internalGuid.Equals(other.internalGuid);
}
public int CompareTo(object obj)
{
return this.internalGuid.CompareTo(obj);
}
public int CompareTo(CustomId other)
{
return this.internalGuid.CompareTo(other.internalGuid);
}
public string ToString(string format, IFormatProvider formatProvider)
{
return this.internalGuid.ToString(format, formatProvider);
}
}
internal static class Program
{
internal static void Main()
{
{
var a = CustomId.NewGuid();
var b = a;
// shows true false
Console.WriteLine("{0} {1}", a == b, object.Equals(a, b));
}
{
var a = Guid.NewGuid();
var b = a;
// shows true true
Console.WriteLine("{0} {1}", a == b, object.Equals(a, b));
}
Console.WriteLine(#"Done.");
Console.ReadLine();
}
}
}
Your code here:
public override bool Equals(object obj)
{
return this.internalGuid.Equals(obj);
}
is going to unwrap itself, but it doesn't unwrap the other instance, so : it will always fail if obj is a CustomId, as the Guid won't expect to be handed a CustomId (it wants a Guid). Perhaps this should be:
public bool Equals(object obj) => obj is CustomId cid && cid == this;
Note that CompareTo should probably be similar:
public int CompareTo(object obj) => obj is CustomId cid ? this.CompareTo(cid) : -1;
The answer from Marc Gravell is correct but I want to note something.
It calls object.Equals(a, b) that in turn calls a == b
This is a wrong assumption. object.Equals(a, b) will call a.Equals(b) if both of them are not null. Subtle difference but it is a difference:
https://referencesource.microsoft.com/#mscorlib/system/object.cs,d9262ceecc1719ab
public static bool Equals(Object objA, Object objB)
{
if (objA==objB) {
return true;
}
if (objA==null || objB==null) {
return false;
}
return objA.Equals(objB);
}
I created class with overriden Equals. The problem is that Distinct method doesn't work for my class.
class MyClass
{
public int Item1 { get; private set; }
public int Item2 { get; private set; }
public MyClass(int item1, int item2)=>(Item1,Item2)=(item1,item2);
public override bool Equals(object obj)
{
var other = obj as MyClass;
if (other == null)
{
return false;
}
return (this.Item1 == other.Item1 && this.Item2 == other.Item2);
}
}
class Program
{
static void Main(string[] args)
{
MyClass x = new MyClass(1, 0);
MyClass y = new MyClass(1, 0);
var list = new List<MyClass>();
list.Add(x);
list.Add(y);
bool b = x.Equals(y)); //True
var distincts = list.Distinct(); //Doesn't work, contains both
}
}
How can I fix that and why it doesn't use my Equals in Distinct?
Distinct docs:
Returns distinct elements from a sequence by using the default equality comparer to compare values.
Let's see what the default equality comparer does:
The Default property checks whether type T implements the System.IEquatable<T> interface and, if so, returns an EqualityComparer<T> that uses that implementation. Otherwise, it returns an EqualityComparer<T> that uses the overrides of Object.Equals and Object.GetHashCode provided by T.
So basically, to make this work, you either:
implement GetHashCode as well
implement IEquatable<T>
Call the overload of Distinct that accepts a custom equality comparer.
If I were you, I would choose the second one because you need to change the least of your code.
class MyClass: IEquatable<MyClass> {
...
public bool Equals(MyClass obj)
{
if (obj == null)
{
return false;
}
return (this.Item1 == obj.Item1 && this.Item2 == obj.Item2);
}
}
You have to override GetHashCode as well:
public override int GetHashCode()
{
return Item1; // or something
}
Distinct first compares the hashcodes, which should be computed faster than the actual Equals. Equals is only further evaulated if the hashcodes are equal for two instances.
You need to implement IEquatable<MyClass> in MyClass and provide your own implementation of GetHashCode and Equals method.
see this for more information.
class MyClass
{
public int Item1 { get; private set; }
public int Item2 { get; private set; }
public MyClass(int item1, int item2)=>(Item1,Item2)=(item1,item2);
public override bool Equals(object obj)
{
var other = obj as MyClass;
if (other == null)
{
return false;
}
return (this.Item1 == other.Item1 && this.Item2 == other.Item2);
}
public override int GetHashCode()
{
return this.Item1;
}
}
I have two classes which both derive from the same parent:
public class People{
public string BetterFoot;
public override bool Equals(object obj){
if (obj == null || this.GetType() != obj.GetType())
return false;
People o = (People)obj;
return (this.BetterFoot == o.BetterFoot);
}
public class LeftiesOrRighties: People{
public string BetterHand;
public override bool Equals(object obj){
if (obj == null || this.GetType() != obj.GetType())
return false;
LeftiesOrRighties o = (LeftiesOrRighties)obj;
return (this.BetterFoot == o.BetterFoot) &&
(this.BetterHand == o.BetterHand)
}
}
public class Ambidextrous: People{
public string FavoriteHand;
}
(There are GetHashCodes in there, too, but I know that they work.)
I'd like to compare collections of them, based on their root Equals():
ThoseOneHanded = new List<LeftiesOrRighties>(){new LeftiesOrRighties(){BetterFoot = "L"}};
ThoseTwoHanded = new List<Ambidextrous>(){new Ambidextrous(){BetterFoot = "L"}};
//using NUnit
Assert.That ((People)ThoseOneHanded[0], Is.EqualTo((People)ThoseTwoHanded[0])));
Unfortunately, this returns false.
Why? Shouldn't the casting make them (for all intents and purposes, if not exactly) the same type, and thus use the base methods? And if not, how do I truly cast the underlying type back to People?
Cast doesn't change the object itself, so the result of GetType will always be the same and so your this.GetType() != obj.GetType() will be true and so the function will return false.
The following logic could potentially gain the behaviour you want (and you don't need to cast to People)
public class People
{
public string BetterFoot;
public override bool Equals(object obj)
{
var o = obj as People;
if (o == null) return false;
return (this.BetterFoot = o.BetterFoot);
}
public class LeftiesOrRighties: People
{
public string BetterHand;
public override bool Equals(object obj)
{
var o = obj as LeftiesOrRighties;
if ( o == null) return base.Equals(obj);
return (this.BetterFoot = o.BetterFoot) && (this.BetterHand = o.BetterHand)
}
}
public class Ambidextrous: People
{
public string FavoriteHand;
}
As Bob Vale pointed out, the cast does not change type.
The standard solution used across the .NET Framework is to use custom object implementing IEqualityComparer or its generic variant. Then your compare/find method takes 2 objects/collections and uses comparer to perform the custom comparison.
I.e. many LINQ methods take custom compare to find/filter objects like
Enumerable.Distinct
public static IEnumerable<TSource> Distinct<TSource>(
this IEnumerable<TSource> source,
IEqualityComparer<TSource> comparer
)
Sample comparer:
class Last3BitsComparer : IEqualityComparer<int>
{
public bool Equals(int b1, int b2)
{
return (b1 & 3) == (b2 & 3);
}
public int GetHashCode(int bx)
{
return bx & 3;
}
}
I have the following scenario:
public class SomeClass {
// Have some other data members as well
public int i ;
}
public class TestClass {
public bool SomeFunction() {
SomeClass a = new SomeClass();
SomeClass b = new SomeClass();
if (a == b) // this is where I am getting compile error
return true;
return false;
}
public static bool operator==(SomeClass a, SomeClass b) {
if (a.i == b.i)
return true;
// compare some other members as well
return false;
}
}
Is this possible to achieve in C#?
Thanks for the help!
No, it's not possible to override an operator from a class that is not involved in the operation.
You can make a class that implements IEualityComparer<SomeClass>, which can be used instead of the standard comparison in some cases, for example in a dictionary:
var x = new Dictionary<SomeClass, string>(new SomeClassEqualityComparer());
If you just want to use the comparison in your own class, you could make it a regular static method instead of overriding an operator:
public static bool SomeClassEqual(SomeClass a, SomeClass b) {
if (a.i == b.i) {
return true;
}
// compare some other members as well
return false;
}
Usage:
if (SomeClassEqual(a, b))
To begin with, you can't use return true; on a void method.
Second, overriding operators should be applied to the host class. In your case, inside SomeClass rather than inside TestClass.
Third, when you implement == overriding operator, you should also implement != .
Here is your code, revised and working:
public class SomeClass
{
// Have some other data members as well
public int i;
public static bool operator ==(SomeClass a, SomeClass b)
{
if (a.i == b.i)
return true;
// compare some other members as well
return false;
}
public static bool operator !=(SomeClass a, SomeClass b)
{
return !(a == b);
}
}
public class TestClass
{
public bool SomeFunction()
{
SomeClass a = new SomeClass();
SomeClass b = new SomeClass();
if (a == b) // this is where I am getting compile error
return true;
return false;
}
}
I have defined the following interface:
public interface IHaveAProblem
{
string Issue { get; set; }
}
And here is the implementation of IHaveAProblem:
public class SomeProblem : IHaveAProblem
{
public string Issue { get; set; }
public override bool Equals(object obj)
{
SomeProblem otherObj = obj as SomeProblem;
if (otherObj == null)
{
return false;
}
return this.Issue == otherObj.Issue;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
public static bool operator ==(SomeProblem rhs, SomeProblem lhs)
{
// Null check
if (Object.ReferenceEquals(rhs, null) || Object.ReferenceEquals(lhs, null))
{
if (Object.ReferenceEquals(rhs, null) && Object.ReferenceEquals(lhs, null))
{
// Both are null. They do equal each other
return true;
}
// Only 1 is null the other is not so they do not equal
return false;
}
return rhs.Equals(lhs);
}
public static bool operator !=(SomeProblem rhs, SomeProblem lhs)
{
// Null check
if (Object.ReferenceEquals(rhs, null) || Object.ReferenceEquals(lhs, null))
{
if (Object.ReferenceEquals(rhs, null) && Object.ReferenceEquals(lhs, null))
{
// Both are null. They do equal each other
return false;
}
// Only 1 is null the other is not so they do not equal
return true;
}
return !rhs.Equals(lhs);
}
}
When I use the object, I can get the correct results for the == compare:
SomeProblem firstTest = new SomeProblem()
{
Issue = "Hello World"
};
SomeProblem secondTest = new SomeProblem()
{
Issue = "Hello World"
};
// This is true
bool result = firstTest == secondTest;
However, when I try to compare the interfaces, it is doing a memory compare rather than the operator == on SomeProblem:
IHaveAProblem firstProblem = new SomeProblem()
{
Issue = "Hello World"
};
IHaveAProblem secondProblem = new SomeProblem()
{
Issue = "Hello World"
};
Is it possible to have the interface use the == on SomeProblem rather than a memory compare?
I know I can do a firstProblem.Equals(secondProblem) and get the proper results. However, I am creating a framework and I will not know how it is used in the end. I thought == would work correctly.
The operator == is static. You cannot define static methods for interfaces in C#. Also, for all operators at least one of the argument types needs to be of the same type as the class it is defined in, therefore: No operator overloading for interfaces :(
What you CAN do is use an abstract class instead - and define the operator there. Again, the operator may NOT be virtual (since static methods cannot be virtual...)
[Edited, reason see comment.]
I konw, this is an old question, but all examples provided show how to compare two class instances, and no one points out how to compare two interface instances.
In some cases, this is the DRYest way to compare interfaces.
public interface IHaveAProblem
{
string Issue { get; set; }
}
public class IHaveAProblemComparer : IComparer<IHaveAProblem>, IEqualityComparer<IHaveAProblem>
{
public int Compare(IHaveAProblem x, IHaveAProblem y)
{
return string.Compare(x.Issue, y.Issue);
}
public bool Equals(IHaveAProblem x, IHaveAProblem y)
{
return string.Equals(x.Issue, y.Issue);
}
public int GetHashCode(IHaveAProblem obj)
{
return obj.GetHashCode();
}
}
Usage?
IHaveAProblemComparer comparer = new IHaveAProblemComparer();
List<IHaveAProblem> myListOfInterfaces = GetSomeIHaveAProblemObjects();
myListOfInterfaces.Sort(comparer); // items ordered by Issue
IHaveAProblem obj1 = new SomeProblemTypeA() { Issue = "Example1" };
IHaveAProblem obj2 = new SomeProblemTypeB() { Issue = "Example2" };
bool areEquals = comparer.Equals(obj1, obj2); // False
IIRC (and I could be wrong here), C# interfaces don't allow operator overloading.
But in this case that's okay. The == operator normally maps to reference equality. It sounds like you want value equality, and that means you want to force them to override the .Equals() (and consequently also .GetHashCode()) functions. You do that by having your interface inherit from IEquatable.
Have you tried implementing IComparable?
Like this:
public interface IHaveAProblem : IComparable
{
string Issue { get; set; }
}
And then in the implementation of the class:
public class SomeProblem : IHaveAProblem
{
public string Issue { get; set; }
...
public int CompareTo(object obj)
{
return Issue.CompareTo(((SomeProblem)obj).Issue);
}
}
Note that, this works only when you compare two instances of SomeProblem, but not any other implementations of the IHaveAProblem interface.
Not sure if there could occur a NullReferenceException.