Library with Equals and GetHashCode helper methods for .NET - c#

Google Guava provides nice helpers to implement equals and hashCode like the following example demonstrates:
public int hashCode() {
return Objects.hashCode(lastName, firstName, gender);
}
Is there a similar library for Microsoft .NET?

I don't see why you'd need one. If you want to create a hash-code based on the default GetHashCode for 3 different items, then just use:
Tuple.Create(lastName, firstName, gender).GetHashCode()
That'll boil down to the equivalent of:
int h1 = lastName.GetHashCode();
int h2 = firstName.GetHashCode();
int h3 = gender.GetHashCode();
return (((h1 << 5) + h1) ^ (((h2 << 5) + h2) ^ h3));
Which is pretty reasonable for such a general-purpose combination.
Likewise:
Tuple.Create(lastName, firstName, gender).Equals(Tuple.Create(lastName2, firstName2, gender2))
Would boil down to the equivalent of calling:
return ((lastName == null && lastName2 == null) || (lastName != null && lastName.Equals(lastName2)))
&& ((firstName == null && firstName2 == null) || (firstName != null && firstName.Equals(lastName2)))
&& ((gender == null && gender2 == null) || (gender != null && gender.Equals(lastName2)));
Again, about as good as you could expect.

AFAIK none. However, writing your own shouldn't be too complex (nb using a variation of the Bernstein hash):
public static class Objects
{
public static bool Equals<T>(T item1, T item2, Func<T, IEnumerable<object>> selector)
{
if (object.ReferenceEquals(item1, item2) return true;
if (item1 == null || item2 == null) return false;
using (var iterator1 = selector(item1).GetEnumerator())
using (var iterator2 = selector(item2).GetEnumerator())
{
var moved1 = iterator1.MoveNext();
var moved2 = iterator2.MoveNext();
if (moved1 != moved2) return false;
if (moved1 && moved2)
{
if (!Equals(iterator1.Current, iterator2.Current)) return false;
}
}
return true;
}
public static bool Equals(object item1, object item2)
{
return object.Equals(item1, item2);
}
public static int GetHashCode(params object[] objects)
{
unchecked
{
int hash = 17;
foreach (var item in objects)
{
hash = hash * 31 + item.GetHashCode();
}
return hash;
}
}
}

Related

Sorting based on multiple fields using IComparer [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
I use the below code to sort List<DataAccessViewModel> list.
Here is the sort order :
PriorityScore
MName
CName
FName
It works as expected.
public int Compare(DataAccessViewModel x, DataAccessViewModel y)
{
if (x == null || y == null)
{
return 0;
}
return x.CompareTo(y);
}
public int CompareTo(DataAccessViewModel mod)
{
int retval = (int)(this.PriorityScore?.CompareTo(mod.PriorityScore));
if(retval != 0)
return retval;
else
{
retval = (this.MName ?? "zzzzzzzzzzzzz").CompareTo(mod.MName ?? "zzzzzzzzzzzzz");
if (retval != 0)
return retval;
else
{
retval = (this.CName ?? "zzzzzzzzzzzzz").CompareTo(this.CName ?? "zzzzzzzzzzzzz");
if (retval != 0)
return retval;
else
retval = (this.FName ?? "zzzzzzzzzzzzz").CompareTo(this.FName ?? "zzzzzzzzzzzzz");
}
}
return retval;
}
But the code looks clunky to me. Is there any better way of doing it or is this it ?
There's a issue in the current code:
public int Compare(DataAccessViewModel x, DataAccessViewModel y)
{
if (x == null || y == null)
{
return 0;
}
...
Since null equals to any value then all values are equal:
a == null, null == b => a == b
It's not the rule you want to implement. I suggest something like this
using System.Linq;
...
// static: we don't want "this"
public static int TheCompare(DataAccessViewModel x, DataAccessViewModel y) {
// Special cases, nulls
if (ReferenceEquals(x, y)) // if references are shared, then equal
return 0;
if (null == x) // let null be smaller than any other value
return -1;
if (null == y)
return 1;
// How we compare strings
static int MyCompare(string left, string right) =>
ReferenceEquals(left, right) ? 0
: null == left ? 1 // null is greater than any other string
: null == right ? -1
: string.Compare(left, right);
// Func<int> for lazy computation
return new Func<int>[] {
() => x.PriorityScore?.CompareTo(y.PriorityScore) ?? -1,
() => MyCompare(x.MName, y.MName),
() => MyCompare(x.CName, y.CName),
() => MyCompare(x.FName, y.FName),
}
.Select(func => func())
.FirstOrDefault(value => value != 0);
}
And then for interface we have
public int CompareTo(DataAccessViewModel other) => TheCompare(this, other);
// I doubt if you want to implement both IComparable<T> and
// IComparer<T> but you can easily do it
public int Compare(DataAccessViewModel x, DataAccessViewModel y) =>
TheCompare(x, y);
You're almost there: just skip the else statements which follow a return:
public int CompareTo(DataAccessViewModel mod)
{
// I worry about this line -- if you're sure that PriorityScore is not null,
// why not use this.PriorityScore.Value to make that clear?
int retval = (int)(this.PriorityScore?.CompareTo(mod.PriorityScore));
if (retval != 0)
return retval;
retval = (this.MName ?? "zzzzzzzzzzzzz").CompareTo(mod.MName ?? "zzzzzzzzzzzzz");
if (retval != 0)
return retval;
retval = (this.CName ?? "zzzzzzzzzzzzz").CompareTo(this.CName ?? "zzzzzzzzzzzzz");
if (retval != 0)
return retval;
return (this.FName ?? "zzzzzzzzzzzzz").CompareTo(this.FName ?? "zzzzzzzzzzzzz");
}
You can also handle the nullables properly by using Comparer<T>.Default. This will sort null as equal to other null values, but less than any other object.
public int CompareTo(DataAccessViewModel mod)
{
int retval = Comparer<int?>.Default.Compare(this.PriorityScore, mod.PriorityScore);
if (retval != 0)
return retval;
retval = Comparer<string>.Default.Compare(this.MName, mod.MName);
if (retval != 0)
return retval;
retval = Comparer<string>.Default.Compare(this.CName, mod.CName);
if (retval != 0)
return retval;
return Comparer<string>.Default.Compare(this.FName, mod.FName);
}
Consider leveraging the default behavior of ValueTuples:
IComparer (this should probably not be the same as your class)
public int Compare(DataAccessViewModel x, DataAccessViewModel y)
{
if (x == null && y == null)
{
return 0;
}
if (x == null) return -1;
if (y == null) return 1;
var thisCompareOrder = (x.PriorityScore, x.MName, x.CName, x.FName);
var thatCompareOrder = (y.PriorityScore, y.MName, y.CName, y.FName);
return thisCompareOrder.CompareTo(thatCompareOrder);
}
If you want your class to have these semantics by default, implement IComparable.
public int CompareTo(DataAccessViewModel mod)
{
if(mod == null) return 1;
var thisCompareOrder = (this.PriorityScore, this.MName, this.CName, this.FName);
var thatCompareOrder = (mod.PriorityScore, mod.MName, mod.CName, mod.FName);
return thisCompareOrder.CompareTo(thatCompareOrder);
}
You might also consider making your original class a record, which has value-based semantics by default.

Multithread, Linq to Sql, ConcurrentDictonary Fails Remove

I have a ConcurrentDictionary of Attributes for products. These attributes have the product ID and values for the names of the attribute and any options the attribute has. I have this ConcurrentDictionary because I have threads that are created to handle each attribute in the dictionary by attribute name.
if (knownAttribute.AttributeType.Value.Equals("Product Specification"))
{
Console.WriteLine("Started a thread for: " + knownAttribute.AttributeTypeId + ", " + knownAttribute.Value);
while (true)
{
/* if (AS400SpecificationAttributes.IsEmpty && knownSpecificationBag.IsEmpty && gatherRowsTasks.All(x => x.IsCompleted))
break;*/
AS400SpecificationAttribute AS400SpecificationAttributeWork = null;
AS400SpecificationAttributeWork = knownSpecificationBag.Keys.FirstOrDefault(x => x.AttributeName == knownAttribute.Value);
if (AS400SpecificationAttributeWork != null)
{
var product = ctx.Products.FirstOrDefault(x => x.ProductNumber == AS400SpecificationAttributeWork.ProductNumber);
if (product == null)
continue;
var productAttribute = new ProductAttribute();
productAttribute.Attribute = knownAttribute;
if (AS400SpecificationAttributeWork.AttributeValue != null)
{
var knownAttributeOption = ctx.AttributeOptions.FirstOrDefault(x => x.Attribute.Equals(knownAttribute) && x.Value.Equals(AS400SpecificationAttributeWork.AttributeValue));
if (knownAttributeOption == null)
{
knownAttributeOption = new AttributeOption();
knownAttributeOption.Value = AS400SpecificationAttributeWork.AttributeValue;
knownAttributeOption.Attribute = knownAttribute;
ctx.AttributeOptions.InsertOnSubmit(knownAttributeOption);
ctx.SubmitChanges();
}
productAttribute.AttributeOption = knownAttributeOption;
productAttribute.AttributeOptionId = knownAttributeOption.Id;
}
product.ProductAttributes.Add(productAttribute);
ctx.SubmitChanges();
string tmpstr = null;
if (!knownSpecificationBag.TryRemove(AS400SpecificationAttributeWork, out tmpstr))
Thread.Sleep(50);
}
else
{
if (tryCounter < 5)
{
tryCounter++;
Thread.Sleep(1000);
Console.WriteLine("Thread waiting for work: Product Specification:" + knownAttribute.Value);
continue;
}
else
{
int outVal;
threadTracker.TryRemove("Product Specification:" + knownAttribute.Value, out outVal);
Console.WriteLine("Closing Thread: Product Specification:" + knownAttribute.Value);
break;
}
}
Thread.Sleep(50);
}
It seems like the following Attribute element refuses to be removed.
I don't understand why. If i put it in a while(!dic.tryRemove(ele)) it will forever be stuck and never move from there.
There may be an error somewhere within the thread but I have no idea why.
This statement
if (!knownSpecificationBag.TryRemove(AS400SpecificationAttributeWork, out tmpstr))
will always return true or false. It won't block. That's the behavior of ConcurrentDictionary. It will return false if the key is not in the dictionary.
If you're looping while that method returns false and it's stuck, that means that the item isn't in the dictionary when the loop begins. Either it either was never in the dictionary or that another thread already removed it.
Is your intention to loop until the item is not in the dictionary?
You could try this:
if (!knownSpecificationBag.TryRemove(AS400SpecificationAttributeWork, out tmpstr)
&& !knownSpecificationBag.ContainsKey(AS400SpecificationAttributeWork))
Implement proper equals and gethashcode when using TryRemove
public override int GetHashCode()
{
return new { this.name, this.value, this.group, this.productNumber }.GetHashCode();
}
public bool Equals(AS400SpecificationAttribute other)
{
if (other == null)
return false;
return (this.ProductNumber.Equals(other.productNumber) && ((this.group != null && this.group.Equals(other.AttributeGroup)) || (this.group == null && other.AttributeGroup == null)) && ((this.name!= null && this.name.Equals(other.AttributeName)) || (this.name == null && other.AttributeName == null)) && ((this.value != null && this.value.ToUpper().Equals(other.AttributeValue.ToUpper())) || (this.value == null && other.AttributeValue == null)));
}

c# find in text something or something

I need to search in text, but is it possible to have more than one option in richtextbox.Find("something");? For example richtextbox.Find("something" or "somethingelse");
You can implement something like this (an extension method FindAny for RichTextBox):
public static class RichTextBoxExtensions {
public static int FindAny(this RichTextBox source, params String[] toFind) {
if (null == source)
throw new ArgumentNullException("source");
else if (null == toFind)
throw new ArgumentNullException("toFind");
int result = -1;
foreach (var item in toFind) {
if (null == item)
continue;
int v = source.Find(item);
if ((v >= 0) && ((result < 0) || (v < result)))
result = v;
}
return result;
}
}
....
int result = richtextbox.FindAny("something", "somethingelse");

Convert String To Int in LINQ

I have a LINQ query that queries a DataTable. In the DataTable, the field is a string and I need to compare that to an integer, basically:
if ((electrical >= 100 && electrical <= 135) || electrical == 19)
{
// The device passes
}
the problem is, I am trying to do this in LINQ like this:
var eGoodCountQuery =
from row in singulationOne.Table.AsEnumerable()
where (Int32.Parse(row.Field<String>("electrical")) >= 100 &&
Int32.Parse(row.Field<String>("electrical")) <= 135) &&
Int32.Parse(row.Field<String>("electrical")) != 19 &&
row.Field<String>("print") == printName
select row;
I keep getting the exception:
Input string was not in a correct format
The main problem occurs when electrical == ""
Unfortunately, the framework doesn't provide a nice clean way to handle parsing scenarios where it fails. Of what's provided, they only throw exceptions or use out parameters, both of which does not work well with linq queries. If any one value you're parsing fails, the entire query fails and you just can't really use out parameters. You need to provide a method to handle the parsing without that does not throw and does not require using out parameters.
You can handle this in many ways. Implement it where upon failure, you return some default sentinel value.
public static int ParseInt32(string str, int defaultValue = 0)
{
int result;
return Int32.TryParse(str, out result) ? result : defaultValue;
}
Or what I would recommend, return a nullable value (null indicating it failed).
public static int? ParseInt32(string str)
{
int result;
return Int32.TryParse(str, out result) ? result : null;
}
This simplifies your query dramatically while still leaving it readable.
public bool GetElectricalStatus(string printName)
{
var query =
from row in singulationOne.Table.AsEnumerable()
where row.Field<string>("print") == printName
// using the nullable implementation
let electrical = ParseInt32(row.Field<string>("electrical"))
where electrical != null
where electrical == 19 || electrical >= 100 && electrical <= 135
select row;
return !query.Any();
}
p.s., your use of the Convert.ToInt32() method is incorrect. It is the same as calling Int32.Parse() and does not return a nullable, it will throw on failure.
I would check if the data in the column does not contain leading/trailing whitespaces - i.e. "15 " rather than "15" and if it does (or might do) trim it before trying to convert:
Int32.Parse(row.Field<String>("electrical").Trim())
BTW: not related to the error but I'd use let statement to introduce a local variable and do the conversion once:
let x = Int32.Parse(row.Field<String>("electrical").Trim())
where x >= 100...
I could not get anything to work, so I re-did the whole method:
public bool GetElectricalStatus(string printName)
{
List<object> eGoodList = new List<object>();
var eGoodCountQuery =
from row in singulationOne.Table.AsEnumerable()
where row.Field<String>("print") == printName
select row.Field<String>("electrical");
foreach (var eCode in eGoodCountQuery)
{
if (!string.IsNullOrEmpty(eCode.ToString()))
{
int? eCodeInt = Convert.ToInt32(eCode);
if (eCodeInt != null &&
(eCodeInt >= 100 && eCodeInt <= 135) || eCodeInt == 19)
{
eGoodList.Add(eCode);
}
}
}
if (eGoodList.Count() > 0)
{
return false;
}
else
{
return true;
}
}
The main problem occurs when electrical == ""
Why not make a function that does your evaluation, and call it in your Linq query. Put logic in to check the validity of the data contained within (so if you can't parse the data, it should return false)...
The function:
bool IsInRange(string text, int lower, int upper, params int[] diqualifiers)
{
int value = int.MinValue;
if (!int.TryParse(text, out value)) {
return false;
}
if (!(value >= lower && value <= upper)) {
return false;
}
if (disqualifiers != null && disqualifiers.Any(d => d == value)) {
return false;
}
return true;
}
The Linq query...
var eGoodCountQuery =
from row in singulationOne.Table.AsEnumerable()
where
IsInRange(row.Field<String>("electrical"), 100, 135, 19)
&& row.Field<String>("print") == printName
select row;

How can i implement == and check for null in c# [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicates:
What is “Best Practice” For Comparing Two Instances of a Reference Type?
How do I check for nulls in an '==' operator overload without infinite recursion?
I have a class called "Criterion" and i want to implement == operator,but I'm struggling with the following problem :
When i implement the == operator, I'm checking if one or both of my instances are null, but when i do it, it causes a recursive call for == and then i get "StackOverflow" (he he) exception.
Technically i can implement the Equals operator and not override the ==, but the code will be much more readable if i implement the == operator.
Here is my code :
public static bool operator == (Criterion c1, Criterion c2)
{
if (null == c1)
{
if (null == c2)
return true;
return false;
}
if (null == c2)
return false;
if ((c1.mId == c2.mId) && (c1.mName == c2.mName))
return true;
return false;
}
Try ReferenceEquals:
public static bool operator ==(Criterion c1, Criterion c2) {
var nullC1 = ReferenceEquals(null, c1);
var nullC2 = ReferenceEquals(null, c2);
if (nullC1 && nullC2)
return true;
if (!nullC1 && !nullC2)
if (c1.mId == c2.mId && c1.mName == c2.mName)
return true;
return false;
}
I'd do it like this:
bool isC1Null = Object.Equals(c1, null)
bool isC2Null = Object.Equals(c2, null)
if (isC1Null ^ isC2Null)
{
return false
}
if (isC1Null && isC2Null)
{
return true
}
//your code

Categories

Resources