Is there a way to redefine all strings to use a customcomparer, for an entire project? Either with lots of extension methods or a custom string class? Is this a naive approach?
I have a custom string comparer that I use for some dictionaries, which support custom comparers, meaning:
SomeDictionary = new Dictionary<string, string>(CustomComparer);
string someValue = SomeDictionary[somekey];//Uses Custom Comparison, not .Equals
I also made a extension method for contains, which I needed in some places, and you could use like this:
if(someString.Contains(key, new CustomComparer()))
//Do Something
I just tried to this:
if(someString.Contains(key, new CustomComparer()))
someString = someString.Replace("x", "y");
Since I didn't write an extension method for it, Replace() saw "X" in the actual string and said it wasn't equal to "x". I could make an extension for that too, but I fear this is a much bigger problem than I previously considered.
For reference, this is the extension method and the StringComparer
public static class Extensions
{
//Very crude, just calls the CustomComparer directly, ignores the parameter comparer
public static bool Contains(this string source, string match, StringComparer comp)
{
if (match == null)
throw new ArgumentException("Substring", "Substring cannot be null.");
else
return CustomComparer.ModifyString(source).Contains(match);
}
}
public class CustomComparer : StringComparer
{
public static Regex RemoveCharacters { get; set; } = new Regex(#"[\s-_(),.]+");
public override int Compare(string x, string y)
{
return StringComparer.Ordinal.Compare(ModifyString(x), ModifyString(y));
}
public override bool Equals(string x, string y)
{
if (ModifyString(x).Equals(ModifyString(y)))
return true;
else
return false;
}
public override int GetHashCode(string obj)
{
if (obj == null)
return 0;
else
return ModifyString(obj).GetHashCode();
}
public static string ModifyString(string s)
{
return RemoveCharacters.Replace(s.ToLowerInvariant().Trim(), string.Empty);
}
}
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'm using .NET 4.6 and I experienced serious strangeness. I'm trying to get an IEnumerable of all values from a sorted set that are NOT the case-insensitive equivalent of the passed in IEnumerable. From what I understand, that is the job of Except.
I have a class, CaseInsensitiveComparer which is implemented like so:
public class CaseInsensitiveComparer : IComparer<string>, IEqualityComparer<string>
{
public static CaseInsensitiveComparer Instance { get; private set; }
static CaseInsensitiveComparer()
{
Instance = new CaseInsensitiveComparer();
}
public int Compare(string a, string b)
{
var ret = string.Compare(a, b, true);
return ret;
}
public bool Equals(string a, string b)
{
return Compare(a, b) == 0;
}
public int GetHashCode(string a)
{
return a.GetHashCode();
}
}
I use it like this:
public void DotNetWeirdness()
{
var a = new SortedSet<string>();
a.Add("A");
var b = a.Except(new string[] { "a" }, CaseInsensitiveComparer.Instance);
}
The value of b is an IEnumerable containing A. Since I'm asking for all values in SortedSet except for a/A, shouldn't I have an empty set as the result? I'm quite confused by this.
Thanks!
The issue is that you're not overriding GetHashCode. "A".GetHashCode() returns a different value from "a".GetHashCode().
A quick way to fix this is to change the GetHashCode function to this:
public int GetHashCode(string a)
{
return a.ToLower().GetHashCode();
}
Also, do you know about StringComparer? You can change your code to this:
var b = a.Except(new string[] { "a" }, StringComparer.InvariantCultureIgnoreCase);
And not have to worry about implementing your own comparer
Problem: Custom Object implements EqualityComparer and IEquatable, but Dictionary doesn't always use these methods.
Context: I have created a helper class, FilePath for dealing with file paths instead of treating them as strings. The helper class is responsible for determining if two file paths are equal. I then need to store FilePaths in a Dictionary<FilePath, object>.
Goal:
Assert.True(new FilePath(#"c:\temp\file.txt").Equals(
new FilePath(#"C:\TeMp\FIle.tXt"));
var dict = new Dictionary<FilePath, object>
{
{new FilePath(#"c:\temp\file.txt"), new object()}
}
Assert.True(dict.ContainsKey(new FilePath(#"c:\temp\file.txt"));
Assert.True(dict.ContainsKey(new FilePath(#"C:\TeMp\FIle.tXt"));
I've created my FilePath class:
public class FilePath : EqualityComparer, IEquatable
{
private string _fullPath;
public FilePath (string fullPath)
{
_fullPath = Path.GetFullPath(fullPath);
}
public override bool Equals(FilePath x, FilePath y)
{
if (null == x || null == y)
return false;
return (x._fullPath.Equals(y._fullPath, StringComparison.InvariantCultureIgnoreCase));
}
public override int GetHashCode(FilePath obj)
{
return obj._fullPath.GetHashCode();
}
public bool Equals(FilePath other)
{
return Equals(this, other);
}
public override bool Equals(object obj)
{
return Equals(this, obj as FilePathSimple);
}
}
Question:
Assert 1 and 2 pass, but Assert 3 fails:
Assert.True(dict.ContainsKey(new FilePath(#"C:\TeMp\FIle.tXt"));
I needed to override GetHashCode() as well:
public override int GetHashCode()
{
return _fullPath.ToLower().GetHashCode();
}
References: What's the role of GetHashCode in the IEqualityComparer<T> in .NET?
Compiler Warning (level 3) CS0659
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.
I'm having troubles with the Except() method.
Instead of returning the difference, it returns the original set.
I've tried implementing the IEquatable and IEqualityComparer in the Account class.
I've also tried creating a separate IEqualityComparer class for Account.
When the Except() method is called from main, it doesn't seem to call my custom Equals() method, but when I tried Count(), it did call the custom GetHashCode() method!
I'm sure I made a trivial mistake somewhere and I hope a fresh pair of eyes can help me.
main:
IEnumerable<Account> everyPartnerID =
from partner in dataContext.Partners
select new Account { IDPartner = partner.ID, Name = partner.Name };
IEnumerable<Account> hasAccountPartnerID =
from partner in dataContext.Partners
from account in dataContext.Accounts
where
!partner.ID.Equals(Guid.Empty) &&
account.IDPartner.Equals(partner.ID) &&
account.Username.Equals("Special")
select new Account { IDPartner = partner.ID, Name = partner.Name };
IEnumerable<Account> noAccountPartnerID =
everyPartnerID.Except(
hasAccountPartnerID,
new LambdaComparer<Account>((x, y) => x.IDPartner.Equals(y.IDPartner)));
Account:
public class Account : IEquatable<Account>
{
public Guid IDPartner{ get; set; }
public string Name{ get; set; }
/* #region IEquatable<Account> Members
public bool Equals(Account other)
{
return this.IDPartner.Equals(other.IDPartner);
}
#endregion*/
}
LambdaComparer:
public class LambdaComparer<T> : IEqualityComparer<T>
{
private readonly Func<T, T, bool> _lambdaComparer;
private readonly Func<T, int> _lambdaHash;
public LambdaComparer(Func<T, T, bool> lambdaComparer) :
this(lambdaComparer, o => o.GetHashCode())
{
}
public LambdaComparer(Func<T, T, bool> lambdaComparer, Func<T, int> lambdaHash)
{
if (lambdaComparer == null)
throw new ArgumentNullException("lambdaComparer");
if (lambdaHash == null)
throw new ArgumentNullException("lambdaHash");
_lambdaComparer = lambdaComparer;
_lambdaHash = lambdaHash;
}
public bool Equals(T x, T y)
{
return _lambdaComparer(x, y);
}
public int GetHashCode(T obj)
{
return _lambdaHash(obj);
}
}
Basically your LambdaComparer class is broken when you pass in just a single function, because it uses the "identity hash code" provider if you don't provide anything else. The hash code is used by Except, and that's what's causing the problem.
Three options here:
Implement your own ExceptBy method and then preferably contribute it to MoreLINQ which contains that sort of thing.
Use a different implementation of IEqualityComparer<T>. I have a ProjectionEqualityComparer class you can use in MiscUtil - or you can use the code as posted in another question.
Pass a lambda expression into your LambdaComparer code to use for the hash:
new LambdaComparer<Account>((x, y) => x.IDPartner.Equals(y.IDPartner)),
x => x.IDPartner.GetHashCode());
You could also quickly fix your LambdaComparer to work when only the equality parameters are supplied like this:
public LambdaComparer(Func<T, T, bool> lambdaComparer) :
this(lambdaComparer, o => 1)
{
}
Look here, how to use and implementing IEqualityComparer in way with linq.Except and beyond.
https://www.dreamincode.net/forums/topic/352582-linq-by-example-3-methods-using-iequalitycomparer/
public class Department {
public string Code { get; set; }
public string Name { get; set; }
}
public class DepartmentComparer : IEqualityComparer {
// equal if their Codes are equal
public bool Equals(Department x, Department y) {
// reference the same objects?
if (Object.ReferenceEquals(x, y)) return true;
// is either null?
if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
return false;
return x.Code == y.Code;
}
public int GetHashCode(Department dept) {
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
// if null default to 0
if (Object.ReferenceEquals(dept, null)) return 0;
return dept.Code.GetHashCode();
}
}
IEnumerable<Department> deptExcept = departments.Except(departments2,
new DepartmentComparer());
foreach (Department dept in deptExcept) {
Console.WriteLine("{0} {1}", dept.Code, dept.Name);
}
// departments not in departments2: AC, Accounts.
IMO, this answer above is the simplest solution compared to other solutions for this problem. I tweaked it such that I use the same logic for the Object class's Equals() and GetHasCode(). The benefit is that this solution is completely transparent to the client linq expression.
public class Ericsson4GCell
{
public string CellName { get; set; }
public string OtherDependantProperty { get; set; }
public override bool Equals(Object y)
{
var rhsCell = y as Ericsson4GCell;
// reference the same objects?
if (Object.ReferenceEquals(this, rhsCell)) return true;
// is either null?
if (Object.ReferenceEquals(this, null) || Object.ReferenceEquals(rhsCell, null))
return false;
return this.CellName == rhsCell.CellName;
}
public override int GetHashCode()
{
// If Equals() returns true for a pair of objects
// then GetHashCode() must return the same value for these objects.
// if null default to 0
if (Object.ReferenceEquals(this, null)) return 0;
return this.CellName.GetHashCode();
}
}