HashSet.Remove not working with EqualityComparer - c#

I am expecting a HashSet that has been created with a specified EqualityComparer to use that comparer on a Remove operation. Especially since the Contains operations returns true!
Here is the code I am using:
public virtual IEnumerable<Allocation> Allocations { get { return _allocations; } }
private ICollection<Allocation> _allocations;
public Activity(IActivitySubject subject) { // constructor
....
_allocations = new HashSet<Allocation>(new DurationExcludedEqualityComparer());
}
public virtual void ClockIn(Allocation a)
{
...
if (_allocations.Contains(a))
_allocations.Remove(a);
_allocations.Add(a);
}
Below is some quick and dirty LINQ that gets me the logic I want, but I am guessing the HashSet remove based on the EqualityComparer would be significantly faster.
public virtual void ClockIn(Allocation a)
{
...
var found = _allocations.Where(x => x.StartTime.Equals(a.StartTime) && x.Resource.Equals(a.Resource)).FirstOrDefault();
if (found != null)
{
if (!Equals(found.Duration, a.Duration))
{
found.UpdateDurationTo(a.Duration);
}
}
else
{
_allocations.Add(a);
}
Can anyone suggest why the Remove would fail when the Contains succeeds?
Cheers,
Berryl
=== EDIT === the comparer
public class DurationExcludedEqualityComparer : EqualityComparer<Allocation>
{
public override bool Equals(Allocation lhs, Allocation rhs)
{
if (ReferenceEquals(null, rhs)) return false;
if (ReferenceEquals(lhs, null)) return false;
if (ReferenceEquals(lhs, rhs)) return true;
return
lhs.StartTime.Equals(rhs.StartTime) &&
lhs.Resource.Equals(rhs.Resource) &&
lhs.Activity.Equals(rhs.Activity);
}
public override int GetHashCode(Allocation obj) {
if (ReferenceEquals(obj, null)) return 0;
unchecked
{
var result = 17;
result = (result * 397) ^ obj.StartTime.GetHashCode();
result = (result * 397) ^ (obj.Resource != null ? obj.Resource.GetHashCode() : 0);
result = (result * 397) ^ (obj.Activity != null ? obj.Activity.GetHashCode() : 0);
return result;
}
}
}
=== UPDATE - FIXED ===
Well, the good news is that HashSet is not broken and works exactly as it should. The bad news, for me, is how incredibly stupid I can be when not being able to see the forest while examining the leaves on the trees!
The answer is actually in the posted code above, if you look at the class creating & owning the HashSet, and then taking another look at the Comparer to find out what is wrong with it. Easy points for the first person to spot it.
Thanks to all who looked at the code!

Well, your code that "works" appears to look at StartTime and Resource while ignoring Activity, whereas your IEqualityComparer<Allocation> implementation looks at all three. Could your problem be related to that?
Also: are your StartTime, Resource, and Activity properties unchanging? Otherwise, since they affect your GetHashCode result, I think you run the risk of breaking your HashSet<Allocation>.

Related

Intersect between two lists not working

I have two lists see below.....result is coming back as empty
List<Pay>olist = new List<Pay>();
List<Pay> nlist = new List<Pay>();
Pay oldpay = new Pay()
{
EventId = 1,
Number = 123,
Amount = 1
};
olist.Add(oldpay);
Pay newpay = new Pay ()
{
EventId = 1,
Number = 123,
Amount = 100
};
nlist.Add(newpay);
var Result = nlist.Intersect(olist);
any clue why?
You need to override the Equals and GetHashCode methods in your Pay class, otherwise Intersect doesn't know when 2 instances are considered equal. How could it guess that it is the EventId that determines equality? oldPay and newPay are different instances, so by default they're not considered equal.
You can override the methods in Pay like this:
public override int GetHashCode()
{
return this.EventId;
}
public override bool Equals(object other)
{
if (other is Pay)
return ((Pay)other).EventId == this.EventId;
return false;
}
Another option is to implement an IEqualityComparer<Pay> and pass it as a parameter to Intersect:
public class PayComparer : IEqualityComparer<Pay>
{
public bool Equals(Pay x, Pay y)
{
if (x == y) // same instance or both null
return true;
if (x == null || y == null) // either one is null but not both
return false;
return x.EventId == y.EventId;
}
public int GetHashCode(Pay pay)
{
return pay != null ? pay.EventId : 0;
}
}
...
var Result = nlist.Intersect(olist, new PayComparer());
Intersect is probably only adding objects when the same instance of Pay is in both List. As oldPay and newPay are instantiated apart they're considered not equal.
Intersect uses the Equals method to compare objects. If you don't override it it keeps the same behavior of the Object class: returning true only if both are the same instance of the object.
You should override the Equals method in Pay.
//in the Pay class
public override bool Equals(Object o) {
Pay pay = o as Pay;
if (pay == null) return false;
// you haven't said if Number should be included in the comparation
return EventId == pay.EventId; // && Number == pay.Number; (if applies)
}
Objects are reference types. When you create two objects, you have two unique references. The only way they would ever compare equal is if you did:
object a = new object();
object b = a;
In this case, (a == b) is true. Read up on reference vs value types, and objects
And to fix your issue, override Equals and GetHashCode, as Thomas Levesque pointed out.
As others have noted, you need to provide the appropriate overrides to get Intersect to work correctly. But there is another way if you don't want to bother with overrides and your use case is simple. This assumes you want to match items on EventId, but you can modify this to compare any property. Note that this approach is likely more expensive than calling Intersect, but for small data sets it may not matter.
List<Pay> intersectedPays = new List<Pay>();
foreach (Pay o in olist)
{
var intersectedPay = nlist.Where(n => n.EventId == o.EventId).SingleOrDefault();
if (intersectedPay != null)
intersectedPays.Add(intersectedPay);
}
List<Pay> result = intersectedPays;

Sorting custom columns in a DataGridView bound to a BindingList

I have a DataGridView which is data-bound to a BindingList. My DataGridView also has a couple of custom columns that I have added. These are not data-bound, but rather are generated based on items in my BindingList (ie: an item in my BindingList of type A has a property of type B; my custom column shows B.Name (EDIT: In this case, "Name" is a property of the class B, and thus the property represented by the column is not directly found in the items in the BindingList)).
I need to be able to sort all of the columns in my DataGridView. DataGridView has two sort methods: Sort(IComparer), and Sort(DataGridViewColumn, ListSortDirection). I use the second one to sort my data-bound columns, but it of course throws an exception when used on a non-data-bound column. The first method will throw an exception if DataSource is not null.
So neither of DataGridView's built-in Sort methods will work as far as I can tell. How else can I sort my grid based on my custom columns?
EDIT:
What I do at the moment is handle the click on the column header, following the instructions seen here: http://msdn.microsoft.com/en-us/library/system.windows.forms.datagridview.columnheadermouseclick.aspx
The problem arises on the line:
dataGridView1.Sort(newColumn, direction);
Things work great when newColumn holds one of the properties of an object in my BindingList. But in order to sort one of my custom columns, I will have to avoid this line altogether, and find some other way to sort the data grid based on that column. Does that mean having to build my own sort function? That seems like it may be a humongous pain.
FINAL Edit/Answer: I can't think of a way to do this while still using the sorting mechanism built into the DataGridView. If I were in your shoes, I would probably just change the SortMode of each column to "Programmatic" and then handle the "ColumnHeaderMouseClick" yourself. At that point you should call a sort method in your modified BindingList class which will perform the sorting as necessary according the which column was clicked. This avoids using the Sort method of the DGV and sorts the underlying list directly.
Full Discourse Located in the comments section. Original Answer follows immediately:
EDIT: Due to some confusion about the problem and subsequent discussion about it, I have a new suggestion down in the comments of this Answer. I'm leaving the original answer I posted so that we can reference it.
I recently had to do this - and I won't lie it was a real pain. I did come up with a solution (with help from some friends here at SO) so here goes. I created a new IComparer based interface that allows you to specify both a column and a direction. I only did this because I need my sorting code to be as generic as possible - I have TWO grids that need to sort like this, and I don't want to maintain twice the code. Here is the interface, quite simple:
public interface IByColumnComparer : IComparer
{
string SortColumn { get; set; }
bool SortDescending { get; set; }
}
Obviously, if you're not worried about keeping things generic (you probably should) than this isn't strictly necessary. Then, I built a new class that is based on BindingList<>. This allowed me to override the sorting code and provide my own IByColumnComparer on a column by column basis which is what allowed for the flexibility I needed. Check this out:
public class SortableGenericCollection<T> : BindingList<T>
{
IByColumnComparer GenericComparer = null;
public SortableGenericCollection(IByColumnComparer SortingComparer)
{
GenericComparer = SortingComparer;
}
protected override bool SupportsSortingCore
{
get
{
return true;
}
}
protected override bool IsSortedCore
{
get
{
for (int i = 0; i < Items.Count - 1; ++i)
{
T lhs = Items[i];
T rhs = Items[i + 1];
PropertyDescriptor property = SortPropertyCore;
if (property != null)
{
object lhsValue = lhs == null ? null :
property.GetValue(lhs);
object rhsValue = rhs == null ? null :
property.GetValue(rhs);
int result;
if (lhsValue == null)
{
result = -1;
}
else if (rhsValue == null)
{
result = 1;
}
else
{
result = GenericComparer.Compare(lhs, rhs);
}
if (result >= 0)
{
return false;
}
}
}
return true;
}
}
private ListSortDirection sortDirection;
protected override ListSortDirection SortDirectionCore
{
get
{
return sortDirection;
}
}
private PropertyDescriptor sortProperty;
protected override PropertyDescriptor SortPropertyCore
{
get
{
return sortProperty;
}
}
protected override void ApplySortCore(PropertyDescriptor prop,
ListSortDirection direction)
{
sortProperty = prop;
sortDirection = direction;
GenericComparer.SortColumn = prop.Name;
GenericComparer.SortDescending = direction == ListSortDirection.Descending ? true : false;
List<T> list = (List<T>)Items;
list.Sort(delegate(T lhs, T rhs)
{
if (sortProperty != null)
{
object lhsValue = lhs == null ? null :
sortProperty.GetValue(lhs);
object rhsValue = rhs == null ? null :
sortProperty.GetValue(rhs);
int result;
if (lhsValue == null)
{
result = -1;
}
else if (rhsValue == null)
{
result = 1;
}
else
{
result = GenericComparer.Compare(lhs, rhs);
}
return result;
}
else
{
return 0;
}
});
}
protected override void RemoveSortCore()
{
sortDirection = ListSortDirection.Ascending;
sortProperty = null;
}
}
Now, as you can see in the ApplySortCore method, I am receiving the column and the direction directly from the DataGridView - meaning that I am not calling this programmatically. That doesn't sound like what you want to do, but you could easily modify this code if you need to call it programmatically and pass the appropriate IByColumnComparer. My point in showing you all this is so you can understand how to modify the sorting algorithm, which is quite useful.
Special thanks to #MartinhoFernandes for the suggestions concerning making this class more generic.

PropertyCopy Generically Change String Values from Null to Empty

I've ran into an issue where code classes outside of my control use strings that are null so when they become referenced for example, "string.Length", causes an error. Rather than write a check for the possible 100 fields on average, with nested classes, I thought maybe I could create something easier. I had an idea...
If you've done any research into copying objects PropertyCopy, along with a few others, is an extremely common find. I currently use the class mentioned above. I was wondering if it could be modified to simply go:
if stringPropertyValue is null then set stringPropertyValue equal to string.Empty.
My understanding is limited. I've been doing research to solve my issue but no real good ideas. Can my idea work? Is there a better way? How would it be done if it could?
Update:
Based on a response below I have created this class which I am currently going to use.
public static void DenullifyStringsToEmpty<T>(this T instance)
{
//handle properties
foreach (var filteredProperties in instance.GetType().GetProperties().Where(p =>
(p.PropertyType.IsClass || p.PropertyType.IsInterface || p.PropertyType == typeof(string))))
{
if (filteredProperties.PropertyType == typeof(string))
{
if (filteredProperties.GetValue(instance, null) == null)
{
filteredProperties.SetValue(instance, string.Empty, null);
}
}
else
{
filteredProperties.GetValue(instance, null).DenullifyStringsToEmpty();
}
}
//handle fields
foreach (var filteredFields in instance.GetType().GetFields().Where(f =>
(f.FieldType.IsClass || f.FieldType.IsInterface || f.FieldType == typeof(string))))
{
if (filteredFields.FieldType == typeof(string))
{
if (filteredFields.GetValue(instance) == null)
{
filteredFields.SetValue(instance, string.Empty);
}
}
else
{
filteredFields.GetValue(instance).DenullifyStringsToEmpty();
}
}
}
I know that reflection can be heavy and until we have an issue I think this solution will work great. This is an extension (thanks to the comments below).
Thanks for the input.
Couldn't you just create a simple extension method?
public static string NullToEmpty(this string possibleNullString)
{
return possibleNullString ?? string.Empty;
}
Use this, when accessing the string properties of that third party classes, e.g.:
var length = instanceOfThirdPartyClass.StringProperty.NullToEmpty().Length;
Update:
Now that I understand what you want ;-)
Have a look at this:
public static void DenullStringProperties<T>(this T instance)
{
foreach(var propertyInfo in instance.GetType().GetProperties().
Where(p => p.PropertyType == typeof(string))
{
var value = propertyInfo.GetValue(instance, null);
if(value == null)
value = string.Empty;
propertyInfo.SetValue(instance, value, null);
}
}
You could call it like this:
instanceOfThirdPartyClass.DenullStringProperties();
But I still think you should go with the first approach, because I really don't see a reason to do such heavy lifting during runtime (reflection isn't cheap), just because you are lazy about typing during development :) Additionally, you can't be sure that the properties will stay non null after you have called DenullStringProperties (multi-threading, calls to methods of the object, ...). The first approach checks for null and handles it just as it is needed.

Cross referencing structure from single data input

Another one of my can't see the wood for the trees questions.
Background
I've got a requirement to query a predefined structure for cooking equivalents across (at the moment) a small range of cultures. The structure won't change but the possibility of other cultures and/or equivalents being introduced is a very strong possibility.
Problem
I would like to put in place a 2nd 'structure' that would allow me to retain the core of what I've been given, but at the same time, allow me to intelligently input the equivalent measurements only once. In the example below, I've only created an equivalent for a UK measurement to return it's partner Metric and US counterparts. My aim would be that from this single input, the structure would be able to spit back a UK equivalent if given a US counterpart etc, etc.
Question
Is it asking too much of such an implicit structure to be able to operate in this fashion. Is it bad practice to ask for such a thing?? What would be your approach to solving such a dilema. The end game would be that any equivalent should be produced as a single liner along the lines of:
// this should produce a value of **CupLitre**
var eqv = conversion["CupUS"][LocaleM.Metric];
Anyway, without further ado:
The Example
Whip up a console app and paste the code below into it (.net v3.5).
using System;
using System.Collections.Generic;
using UnitOfMeasurements;
namespace UnitOfMeasurements
{
[Flags]
public enum LocaleM
{
None = 0, //(0000)
Metric = 1, //(0001)
UK = 2, //(0010)
US = 4, //(0100)
}
}
class Program
{
public static void Main(string[] args)
{
// single structure representing UK equivalents
// this should be extensible to any equivalent
var conversionUK
= new Dictionary<string, Dictionary<LocaleM, string>>
{
{"PintUK",
new Dictionary<LocaleM, string>
{
{LocaleM.US, "PintUS"},
{LocaleM.Metric, "Litre"}
}
},
{"FlOzUK",
new Dictionary<LocaleM, string>
{
{LocaleM.US, "FlOzUS"},
{LocaleM.Metric, "MilliLitre"}
}
},
{"CupUK",
new Dictionary<LocaleM, string>
{
{LocaleM.US, "CupUS"},
{LocaleM.Metric, "CupLitre"}
}
}
};
// basic printout of results
Console.WriteLine(string.Format("{0}\t{1}\t{2}", "Key","US","Metric"));
foreach (var item in conversionUK)
{
Console.WriteLine(string.Format("{0}\t{1}\t{2}",
item.Key,
item.Value[LocaleM.US],
item.Value[LocaleM.Metric]));
}
Console.WriteLine(string
.Format("Here's an example of our direct 'query' - {0}",
conversionUK["CupUK"][LocaleM.Metric]));
Console.Read();
}
}
Good luck - looking fwd to some simple but elegant answers.
[edit] - i must add that the requirement for the inputs ("PintUK", "CupMetric") etc, to be strings is a must as these values will be driving (IVolumeUnit)Activator.CreateInstance(eqv) methods further downstream to create concrete conversion classes of the given string names.
Following approach assumes that all your mappings are bidirectional and if a->b and b->c then a->c
I would introduce a concept of something like default name (cross-culture) for each your item to convert. For example in your code these default names could be 'Pint', 'FlOz' and 'Cup':
UPDATE: replaced custom MapKey class with .Net tuple to make code simplier.
class MapClass
{
private readonly Dictionary<string, string> _localToDefaultNameMap = new Dictionary<string, string>();
private readonly Dictionary<Tuple<string, LocaleM>, string> _defaultNameToLocalMap = new Dictionary<Tuple<string, LocaleM>, string>();
public void AddMapping(string defaultName, LocaleM locale, string localName)
{
_localToDefaultNameMap.Add(localName, defaultName);
_defaultNameToLocalMap.Add(Tuple.Create(defaultName, locale), localName);
}
// maps from source name to target
public string Map(string sourceLocalName, LocaleM targetLocale)
{
string defaultName = _localToDefaultNameMap[sourceLocalName];
var mapKey = Tuple.Create(defaultName, targetLocale);
var localName = _defaultNameToLocalMap[mapKey];
return localName;
}
}
Usage:
// Creating map:
var map = new MapClass();
map.AddMapping("Pint", LocaleM.UK, "PintUK");
map.AddMapping("Pint", LocaleM.US, "PintUS");
map.AddMapping("Pint", LocaleM.Metric, "Litre");
string ukPintMappedToUS = map.Map("PintUK", LocaleM.US);
UPDATE 2 MapKey equality members generated by resharper:
class MapKey
{
...
public bool Equals(MapKey other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(other.Locale, Locale) && Equals(other.Key, Key);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof (MapKey)) return false;
return Equals((MapKey) obj);
}
public override int GetHashCode()
{
int result = 0;
result = (result * 397) ^ Locale.GetHashCode();
result = (result * 397) ^ (Key != null ? Key.GetHashCode() : 0);
return result;
}
}

c# compare the data in two object models

I have a dialog, when spawned it gets populated with the data in an object model. At this point the data is copied and stored in a "backup" object model. When the user has finished making their changes, and click "ok" to dismiss the dialog, I need a quick way of comparing the backup object model with the live one - if anything is changed I can create the user a new undo state.
I don't want to have to go and write comparison function for every single class in the object model if possible.
If I serialised both object models and they were identical but stored in different memory locations would they be equal? Does some simple way exist to compare two serialised object models?
I didn't bother with a hash string but just a straight Binary serialisation works wonders. When the dialog opens serialise the object model.
BinaryFormatter formatter = new BinaryFormatter();
m_backupStream = new MemoryStream();
formatter.Serialize(m_backupStream,m_objectModel);
Then if the user adds to the object model using available controls (or not). When the dialog closes you can compare to the original serialisation with a new one - this for me is how i decide whether or not an Undo state is required.
BinaryFormatter formatter = new BinaryFormatter();
MemoryStream liveStream = new MemoryStream();
formatter.Serialize(liveStream,m_objectModel);
byte[] streamOneBytes = liveStream.ToArray();
byte[] streamTwoBytes = m_backupStream.ToArray();
if(!CompareArrays(streamOneBytes, streamTwoBytes))
AddUndoState();
And the compare arrays function incase anybody needs it - prob not the best way of comparing two arrays im sure.
private bool CompareArrays(byte[] a, byte[] b)
{
if (a.Length != b.Length)
return false;
for (int i = 0; i < a.Length;i++)
{
if (a[i] != b[i])
return false;
}
return true;
}
I'd say the best way is to implement the equality operators on all classes in your model (which is usually a good idea anyway if you're going to do comparisons).
class Book
{
public string Title { get; set; }
public string Author { get; set; }
public ICollection<Chapter> Chapters { get; set; }
public bool Equals(Book other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(other.Title, Title) && Equals(other.Author, Author) && Equals(other.Chapters, Chapters);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof (Book)) return false;
return Equals((Book) obj);
}
public override int GetHashCode()
{
unchecked
{
int result = (Title != null ? Title.GetHashCode() : 0);
result = (result*397) ^ (Author != null ? Author.GetHashCode() : 0);
result = (result*397) ^ (Chapters != null ? Chapters.GetHashCode() : 0);
return result;
}
}
}
This snippet is auto-generated by ReSharper, but you can use this as a basis. Basically you will have to extend the non overriden Equals method with your custom comparison logic.
For instance, you might want to use SequenceEquals from the Linq extensions to check if the chapters collection is equal in sequence.
Comparing two books will now be as simple as saying:
Book book1 = new Book();
Book book2 = new Book();
book1.Title = "A book!";
book2.Title = "A book!";
bool equality = book1.Equals(book2); // returns true
book2.Title = "A different Title";
equality = book1.Equals(book2); // returns false
Keep in mind that there's another way of implementing equality: the System.IEquatable, which is used by various classes in the System.Collections namespace for determining equality.
I'd say check that out as well and you're well on your way!
I understand your question to be how one can compare two objects for value equality (as opposed to reference equality) without prior knowledge of the types, such as if they implement IEquatable or override Equals.
To do this I recommend two options:
A. Use an all-purpose serialization class to serialize both objects and compare their value. For example I have a class called XmlSerializer that takes any object and serializes its public properties as an XML document. Two objects that have the same values and possibly the same reference will have the same values in this sense.
B. Using reflection, compare the values of all of the properties of both objects, like:
bool Equal(object a, object b)
{
// They're both null.
if (a == null && b == null) return true;
// One is null, so they can't be the same.
if (a == null || b == null) return false;
// How can they be the same if they're different types?
if (a.GetType() != b.GetType()) return false;
var Props = a.GetType().GetProperties();
foreach(var Prop in Props)
{
// See notes *
var aPropValue = Prop.GetValue(a) ?? string.Empty;
var bPropValue = Prop.GetValue(b) ?? string.Empty;
if(aPropValue.ToString() != bPropValue.ToString())
return false;
}
return true;
}
Here we're assuming that we can easily compare the properties, like if they all implement IConvertible, or correctly override ToString. If that's not the case I would check if they implement IConvertible and if not, recursively call Equal() on the properties.
This only works if you're content with comparing public properties. Of course you COULD check private and protected fields and properties too, but if you know so little about the objects you're probably asking for trouble but doing so.

Categories