Multiple objects with same hash code in a C# HashSet? - c#

I'm using a Syncfusion grid control's selections to get a list of our data objects. When we multi-select objects, for some reason the objects are being double reported. The below code returns one entry for a single selection, four (!) for two selected objects, six (!) for three, etc.
There seems to be a minor bug in the selection code that is causing a multi-selection to be reported twice in this.BaseGridControl.Model.Selections. Yes, the bug should be fixed. But for now, I can work around that; since the objects these rows represent are going into a HashSet anyway, they should be deduplicated there.
HashSet<Bean> selectedBeanSet = new HashSet<Bean>();
for (int i = this.BaseGridControl.Model.Selections.Count - 1; i >= 0; i--) {
selection = this.BaseGridControl.Model.Selections.Ranges[i];
topRow = selection.Top;
bottomRow = selection.Bottom;
for (int j = bottomRow; j >= topRow; j--) {
selectedBeanSet.Add(GetObjectAtRow(j));
}
}
But even after doing this, I still have four rows when there should only be two. Interrogating in the immediate window, I can learn a little about what's there:
selectedBeanSet.Take(4).ToList()[3].GetHashCode()
5177447
selectedBeanSet.Take(4).ToList()[1].GetHashCode()
5177447
These are still the same object! I wrote the .GetHashCode() myself — I know it's referring to properties of Bean and not any kind of reference.
And in case there was any doubt:
selectedBeanSet.GetType().Name
"HashSet`1"
How can the same object be in a HashSet twice? Is the .GetHashCode() I'm calling in the immediate window not the one being used as a key in the HashSet? What's going on here?
EDIT: Here's my .GetHashCode() implementation. I've put a breakpoint in here and stepped through so I know it's getting hit.
public override int GetHashCode() {
return this.Property1.GetHashCode() ^ this.Property2.GetHashCode() ^ this.Property3.GetHashCode();
}
And .Equals():
public override bool Equals(Bean other) {
return this.Property1 == other.Property1 && this.Property2 == other.Property2 && this.Property3 == other.Property3;
}
EDIT 2: The Bean class, sanitized for public consumption. It's a bit strange since for persistence reasons we're storing everything in structs behind the scenes. I believe that fact is immaterial to this problem, however.
public class Bean : AbstractBean<Bean, BeanStruct> {
BeanStruct myStruct;
public int Property1 {
get {
return this.myStruct.property1;
}
set {
this.myStruct.property1 = value;
RaisePropertyChanged("Property1");
}
}
public string Property2 {
get {
return this.myStruct.property2;
}
set {
this.myStruct.property2 = EngineUtils.FillOrTruncate(value, Bean.Length);
RaisePropertyChanged("Property2");
}
}
public string Property3 {
get {
return this.myStruct.property3;
}
set {
this.myStruct.property3 = EngineUtils.FillOrTruncate(value, Bean.Length);
RaisePropertyChanged("Property3");
}
}
}

Related

Stackoverflow only with very large ArrayLists

I'm using a recursive version of the insertion sort algorithm to sort 5000 objects based upon a randomly generated integer property, but I've been getting a stackoverflow exception only at an ArrayList of this size while working fine for ArrayLists of other sizes.
I used Console.WriteLine to see what the "position" integer goes up to in one of my methods and it ends up at `4719 before skipping a line and giving a stackoverflow exception. How should I get around this?
I should also mention that when testing an iterative version of insertion sort in the same Visual Studio solution and using an ArrayList of the same size of objects I do not get a stackoverflow exception.
My code for the recursive insertion sort is below (AL is the ArrayList):
public void IS()
{
ISRM(0);
}
private void ISRM(int position)
{
if (position == AL.Count)
return;
Console.WriteLine(position);
int PositionNext = position + 1;
ISRMNext(position, PositionNext);
ISRM(position + 1);
}
private void ISRMNext(int position, int PositionNext)
{
if ((PositionNext == 0) || (PositionNext == AL.Count))
return;
Webpage EntryNext = (Webpage)AL[PositionNext];
Webpage EntryBefore = (Webpage)AL[PositionNext - 1];
if (EntryBefore.getVisitCount() < EntryNext.getVisitCount())
{
Webpage temp = EntryBefore;
AL[PositionNext - 1] = AL[PositionNext];
AL[PositionNext] = temp;
}
ISRMNext(position, PositionNext - 1);
}
Well, first of all, sorting through recursive call is a bad idea for several reasons.
As you've already found out, this easily leads to a stack overflow due to limited size of the stack.
It will have poor performance by definition since function call and accompanying allocation of local function context on the stack is much more expensive operation compared to something like while or for operators iterating through plain collection.
These are two reasons why #Zer0 probably suggested it, but there's more to it.
There's ready ArrayList.Sort() method waiting for you that takes custom comparator. All you need is to write said comparator for your custom objects according to whatever rules you want and call Sort(your_comparator). That's it. You do not need to re-invent the wheel implementing your own sorting method itself - unless implementing sorting method is the actual goal of your program... but I honestly doubt it.
So, It could be something like this (not tested!):
class MyComparer : IComparer
{
public int Compare(object x, object y)
{
var _x = ((Webpage) x).getVisitCount();
var _y = ((Webpage) y).getVisitCount();
if (_x < _y)
{
return -1;
}
if (_x > _y)
{
return 1;
}
return 0;
}
}
Usage:
var myAL = new ArrayList();
// ... filling up the myAL
myAL.Sort(new MyComparer());

Axosoft Report Builder (Active Reports 3) - Conditional Results Depending on Field Value

I am trying to create a custom report using Axosoft's Report Builder though I have one big issue to tackle. The report will contain calculated totals and counts of the specific items that are pulled in from the database to display. One such field (Custom_163) is a boolean where true will have it be one type of item (Enhancement) while false will have it as another (Maintenance). I am able to get a count of the total amount of items in the report, but I want to break it down as percentages of the total.
Problem: One boolean field will determine two types of items. Differentiating between them to gather a count of each is where I'm struggling.
Initialized Variables:
private bool _oddRow = true;
private string[] _labels = new string[3];
private int _defectsTotal = 0;
private int _enhanceTotal = 0;
private int _maintenTotal = 0;
Detail Section (where it's creating the rows for the items):
public void Detail1_Format()
{
if (rpt.Fields.Contains("Custom_163") && rpt.Fields["Custom_163"].Value != null)
{
if ((bool)rpt.Fields["Custom_163"].Value == true)
{
//needs something here to add a count towards this type
return enhanceTotal;
}
else
{
//needs something here to add a count towards this type
return maintenTotal;
}
_defectsTotal++;
_enhanceTotal += enhanceTotal;
_maintenTotal += maintenTotal;
// To Color Every Other Row
if (_oddRow)
{
rpt.Sections["Detail1"].BackColor = System.Drawing.Color.FromArgb(224, 224, 224);
}
else
{
rpt.Sections["Detail1"].BackColor = System.Drawing.Color.White;
}
_oddRow = !_oddRow;
// Converting to String Values
if (rpt.Fields["ItemId"].Value == null) return;
}
}
The problem is specifically with these lines:
if((bool)rpt.Fields["Custom_163"].Value == true)
I've changed this a ton from similar conditions:
if(rpt.Fields["Custom_163"].Value.ToString() == "True")
if(rpt.Fields["Custom_163"].Value == true)
if(rpt.Fields["Custom_163"].Value = true)
if(rpt.Fields["Custom_163"].Value == 1)
if(rpt.Fields["Custom_163"].Value.ToString() == "1")
if (Convert.ToBoolean(rpt.Fields["Custom_163"].Value) == true)
etc...
But they don't seem to work as intended. I want it to filter out the items where this field is true so the rest will execute.
Another thing is returning the values from the if-else statement (not working and I've tried using a separate method below that doesn't work either):
public class findTotals
{
public findTotals()
{
}
public int resultTotals()
{
bool item = (bool)rpt.Fields["Custom_163"].Value;
if ( item == true)
{
int enhanceTotal = 1;
return;
}
else
{
int maintenTotal = 1;
return;
}
}
}
(This whole method needs a ton of work TBH).
Lastly, these lines has to be outside of the second if-else or at least somehow returned to the original if in order to be printed to the report:
_enhanceTotal += enhanceTotal;
_maintenTotal += maintenTotal;
The _defectsTotal gets a total count of all the items and that works fine. These two lines though are supposed to add a count based on the above conditions for each type so then the total amount of Enhancements and total amount of Maintenance Items get posted at the end of the report. It doesn't work, if not a casting/conversion error or the conditions force the array(s) out of bounds, it's some other issue. I've played around the order for all these too.
Possible Solutions?
How to safely convert (if needed) the bool values to int so that number can be used to add to a total?
How can I return the values in the if-else so the scope matches up with the variables? Will I need a method?
Thank you in advance.
You should be able to do a string compare against "True". i.e.
if(string.Compare(rpt.Fields["Custom_163"].Value.ToString(), "True") == 0)
I figured out what the issue was.
So basically...
if (Convert.ToBoolean(rpt.Fields["Custom_163"].Value) == true)
{
enhanceTotal++;
}
else
{
maintenTotal++;
}
_defectsTotal++;
(This is all within another if-statement and a bunch of code not relevant to issue.) This works fine and actually returns the count, after that another hurdle was figuring out the labels so it can print into it's placeholders correctly. The whole findTotals method wasn't needed. I didn't need any more variables than the three listed above to make it work. A reason I kept on getting errors was because I tried to use one variable as a label to print into different places within the report which isn't allowed apparently. Instead of using one label and sticking it everywhere, I made several that were identical and made sure to give them different names, etc.
So instead of X in three different report sections, X was X, Y, Z (all identical but different identifer) and stuck it where needed. Man, it seems to obvious now, well thanks again.

changing the value of certain elements of a list

I am looping through a list and would like to add multiple occurrences, should I find them.
so far I have,
public struct optionsSort
{
public string name;
public string date;
public double strike;
public string callPut;
public double size;
public string isin;
}
List<List<optionsSort>> stocks = new List<List<optionsSort>>();
optionsSort tempStock1 = new optionsSort();
List<optionsSort> posCheckOptions = new List<optionsSort>();
then some code, then,
for(int k = 0; k<posCheckOptions.Count; k++)
{
for(int l = 0; l<posCheckOptions[l].Count; l++)
{
if(posCheckOptions[l+1] == null)
{
//finished the iteration
break;
}
else if
(posCheckOptions[k][l + 1].date == posCheckOptions[k][l].date
&& posCheckOptions[k][l + 1].strike == posCheckOptions[k][l].strike
&& posCheckOptions[k][l + 1].callPut == posCheckOptions[k][l].callPut)
{
posCheckOptions[k][l].size = posCheckOptions[k][l].size
+ posCheckOptions[k][l + 1].size;
}
}
}
Basicly, Im looking forward from the start of the list. asking the question, are certain elements of the list at i+1 the same as i, if so, add those elements to i and delete the entire row.
i get this error
"Error 1 Cannot modify the return value of 'System.Collections.Generic.List.this[int]' because it is not a variable C:\Users\WindowsFormsApplication1\WindowsFormsApplication1\ReadCSV.cs 890 25 WindowsFormsApplication1
"
Many Thanks for looking.
I believe your problem is that you are using a mutable struct. Mutable structs are evil.
The simplest solution is to change optionsSort to a class. That should fix the error message.
To explain the error message, when you call posCheckOptions[k][l], since optionsSort is a struct, it returns a copy of the value in the list. When you change size, it will update the copy, but not the one in the list. The copy would then be discarded. The compiler recognizes this and stops you.
I recommend you read up on the differences between reference types and value types.

C# dynamic logic gate

I am working on a program that would allow the user to build digital circuits from virtual logic gates. Every gate would be an instance of a class that represents particular gate type, for example here's how AND class looks:
public class andgate
{
public andgate()
{
inputs = new bool[7];
for (int i = 0; i < 7; i++) inputs[i] = true;
output = (inputs[0] && inputs[1] && inputs[2] && inputs[3] && inputs[4] && inputs[5] && inputs[6]);
}
public bool[] inputs;
public bool output;
}
Every gate has seven inputs but not all of them have to be used (i.e. for a gate with three imputs the remaining four will just be "1" which is AND's neutral element anyway). Each input is a reference to an output of another gate or to an element of bool array (which stores the input vector) so that signal generated by one gate automatically is sent to the following gate. The problem is I also need the signal to be transmitted dynamically within the gate, i.e. if one of the input signals in AND gate is set to 0 then the output automatically has a 0 value. Therefore when you feed a binary vector to the inputs of the cirtuit, it changes the values of the circuit's outputs. Or maybe there's an easier way to simulate a circuit than building it from individual gates? I need it for test pattern generating.
Make the output property read-only:
public bool output
{
get
{
return inputs.All(i => i);
}
}
Instead of ANDing all the inputs, just check if there is any input that is false.
Of course you have to remove the assignment to output in the constructor. This should make your output property "dynamic".
You may also want to change the inputs to bool?[] instead, so that a null value would signify that there is no signal. You will then have to remove the initialization of the input array to all true and change the output return to:
return inputs.All(i => i.GetValueOrDefault(true));
Edited with Tim S's suggestions in the comments
For this, you should use a properties to set/get the inputs, as you can perform additional computations in a property. The state variables you're holding should be private.
public bool[] Inputs {
set {
inputs = value;
}
}
public bool Output {
get {
return inputs[0] && inputs[1] ...
}
}
I would avoid doing this for the Inputs property, since exposing arrays is really exposing information about how the class stores things, which should be avoided where possible for good OOP. A ReadOnlyCollection might be more suitable, for example.
However, I would rethink the design in general, and avoid having some arbitrary # of inputs, 7. Where have you conjured that value from?
A more simplistic approach would be to assume that a gate takes 2 values - A and B, for which you can set the values in the constructor or individual properties.
You then take advantage of the fact that operations on binary logic gates are associative, so an AND gate taking a, b, c, is equivalent to one taking a, b, feeding it's output to another which also takes c. This is how you'd build a circuit in practice anyway - the issue you need to consider is the latency of the gate though.
It sounds like you should add events into your gates, such that when their state changes they're able to notify dependant objects; something like this:
public class AndGate
{
private bool[] inputs;
private bool output;
public bool[] Inputs
{
get
{
return this.inputs;
}
set
{
this.inputs = value;
UpdateOutput();
}
}
public bool Output
{
get
{
return this.output;
}
}
public AndGate()
{
inputs = new bool[7];
for (int i = 0; i < 7; i++) inputs[i] = true;
UpdateOutput();
}
private void UpdateOutput()
{
bool original = output;
output = true;
for(int i=0; i<inputs.Length; i++)
{
output = output && inputs[i];
}
if (original != output)
{
OnChanged(EventArgs.Empty);
}
}
public event GateStateChangedEventHandler StateChanged;
protected virtual void OnChanged(EventArgs e)
{
if (StateChanged != null)
{
StateChanged(this, e);
}
}
}

Basic locking question in C#

The classes:
public class SomeCollection
{
public void IteratorReset()
{
index = -1;
}
public bool IteratorNext()
{
index++;
return index < Count;
}
public int Count
{
get
{
return floatCollection.Count;
}
}
public float CurrentValue
{
get
{
return floatCollection[index];
}
}
public int CurrentIndex
{
get
{
return intCollection[index];
}
}
}
Class that holds reference to 'SomeCollection':
public class ThreadUnsafeClass
{
public SomeCollection CollectionObj
{
get
{
return collectionObj;
}
}
}
Classes ClassA, ClassB and ClassC contain the following loop that iterates over CollectionObj:
for (threadUnsafeClass.CollectionObj.IteratorReset(); threadUnsafeClass.CollectionObj.IteratorNext(); )
{
int currentIntIndex = threadUnsafeClass.CollectionObj.CurrentIndex;
float currentfloatValue = threadUnsafeClass.CollectionObj.CurrentValue;
// ...
}
Since I'm only reading CollectionObj in the 3 classes, I'm using multithreading for speedup, but I'm not quite sure how to enforce thread safety. I added a lock in ThreadUnsafeClass when retrieving CollectionObj, but the application throws an out of range exception.
Any help is appreciated.
Thank you !
You're only reading the CollectionObj property, but then you're mutating the object that the value refers to. See this bit:
for (threadUnsafeClass.CollectionObj.IteratorReset();
threadUnsafeClass.CollectionObj.IteratorNext(); )
Both IteratorReset and IteratorNext mutate SomeCollection by changing the value of index. Basically, you can't do this safely with your current code. Several threads could all call IteratorNext() at the same time, for example. The first call returns true, but before that thread gets a chance to read the values, the other threads make the index invalid.
Why are you using the collection itself for iteration? Typically you'd implement IEnumerable<T> and return a new object in GetEnumerator. That way different threads could each get a different object representing "their" cursor over the same collection. They could all iterate over it, and all see all the values.
The SomeCollection object is being referenced by each of the three classes A,B, and C, each of which is going to try and increment the internal index, causing the error(s). That said, you should be able to read objects in an array from multiple threads with something like the following:
public static object[] sharedList = new object[]{1,2,3,4,5};
public void Worker()
{
int localSum=0;
for(int i=0; i<sharedList.length; i++){
localSum += (int)sharedList[i];
}
}
The important thing here is that each thread will maintain it's own location within the array, unlike with the collectionObj.
Locking the CollectionObj property won't help. One possible problem is that all 3 threads are calling IteratorReset(), which sets the index to -1. Imagine the scenario where A starts the for loop, and gets to the first line in the loop before getting interrupted. Now B comes in and calls IteratorReset(), then gets interrupted to let A run again. Thread A executes the CurrentIndex property, which internally uses index = -1 due to B running. Boom, out of range exception.
There are other ways this can generate bad results, but that's probably the easiest to see. Is the intention to have all three threads go through each item on their own? Or are you expecting A, B, and C to divide up the work (like a consumer queue)?

Categories