Why does modifying one session variable also modify a different session variable? - c#

I have a weird problem. One of my variables is changed via this function (code below). I don't know how it is possible. I want to change Session["p_skok"], but in some way, variable Session["z_skok"] is also changing.
protected void oblicz_parametry()
{
List<string> lista_odnosnik_wartosc = (List<string>)Session["p_wartosc"];
List<Type> lista_odnosnik_typ = (List<Type>)Session["p_typ"];
List<bool> lista_odnosnik_inkrementacja = (List<bool>)Session["p_inkrementacja"];
List<int> lista_odnosnik_nr = (List<int>)Session["p_nr"];
List<int> lista_odnosnik_skok = (List<int>)Session["p_skok"];
List<int> lista_odnosnik_skok_pomocnicza = (List<int>)Session["z_skok"];
if (Session["iteracja"] != null)
{
for (int i = 0; i < lista_odnosnik_inkrementacja.Count(); i++)
{
if (lista_odnosnik_skok[i] == 0 && lista_odnosnik_inkrementacja[i] == true)
{
int zwieksz = lista_odnosnik_nr[i];
//if (lista_odnosnik_typ[i].ToString() == "int")
//{
int zm_pomocnicza = Convert.ToInt32(lista_odnosnik_wartosc[i]) + lista_odnosnik_nr[i];
lista_odnosnik_wartosc[i] = zm_pomocnicza.ToString();
//}
lista_odnosnik_skok[i] = lista_odnosnik_skok_pomocnicza[i] + 1;
}
lista_odnosnik_skok[i]--;
}
Session["p_wartosc"] = lista_odnosnik_wartosc;
Session["p_skok"] = lista_odnosnik_skok;
}
else
{
Session["iteracja"] = 1;
Session["p_wartosc"] = Session["z_wartosc"];
Session["p_inkrementacja"] = Session["z_inkrementacja"];
Session["p_nr"] = Session["z_nr"];
Session["p_skok"] = Session["z_skok"];
oblicz_parametry();
}
}
I did some debugging and found out that decreasing happens because of lista_odnosnik_skok[i]--; part of the code. Do you know what might be happening?

It's a combination of two things. In the else clause you have this:
Session["p_skok"] = Session["z_skok"];
Once you do that, those two variables refer to the same list. Now whatever you do to one list, you're doing to the other, because they're the same list.
Then you're setting Session["iteracja"] = 1;, which means next time the condition if (Session["iteracja"] != null) will be true.
Within that condition you're making changes to lista_odnosnik_skok, which is Session["p_skok"] which is the same list as Session["z_skok"];.
To solve it, don't set the one session variable equal to the other. You could do this:
var existingList = (List<int>)Session["z_skok"];
Session["p_skok"] = new List<int>(existingList);
Now you've created a new list that contains the same elements as the original list. They're two distinct lists, so modifying one doesn't modify the other.
It's worth noting that this works because int is a value type, not a reference type, so when you add the values from one list to the other list you're creating new values. You can modify the values in one list without modifying the others.
If the items in the list were reference types you'd still have a similar problem. You'd have two lists (good) but they would both contain references to the same objects (probably bad). You'd be able to add or delete on one list without modifying the other list, but modifying the item in the list would modify the item in the other list, because they're both the same item.
But again, that's just for explanation. The list contains int so in this case it's not a problem.
It might also be easier to follow if you read everything you need out of Session into variables at the beginning, and then save everything back to Session at the end. That way you're not trying to keep track of both local variables and session variables at the same time. It's confusing, which means it's easier to make mistakes.
You might even find it helpful to define a new class that contains all of these lists. Retrieve that one object from session (if it's null create new one), and when you're done working with it, save the whole object. It's the same as what you're doing, but now you're only dealing with one session variable instead of five, and it's even easier to keep track of.

At some point in the lifecycle of your program you set these two session variables equivalent:
Session["p_skok"] = Session["z_skok"];
This line of code is in the else block. The session will survive for a long time, perhaps hours:
After this line of code has run they will both refer to the same list object in memory and any changes inflicted on the contents of either Session["p_skok"] or lista_odnosnik_skok, or Session["z_skok"]; or lista_odnosnik_skok_pomocnicza will all have the same effect because they're all references to the same List<int>

Related

List inside a List returns Empty after calling List.Clear()

In my code,I do have a List, named lstDetails.
It contains another List, named lstcheckdetails.
As output, lstcheckdetails returns an empty list as it calls Clear function.
Until now I thought the list will get committed after adding data to it.
Can you help me out?
List<AttendanceDetails> lstDetails = new List<AttendanceDetails>();
List<CheckDetails> lstcheckdetails = new List<CheckDetails>();
CheckDetails checkdetails = new CheckDetails();
for loop
{
if(condition)
{
checkdetails.Item1 = item1;
checkdetails.Item2 = item2;
lstcheckdetails.Add(checkdetails);
}
else
{
lstDetails.Add(new AttendanceDetails { TimeCard = lstcheckdetails });
lstcheckdetails.Clear();
}
}
I would recommend reading up on how reference types work in C#. Jon Skeet as always, is a good person to listen to. References and Values - however there are plenty of sources on this out there
To make a quick summary reference types as their name implies are passed around by reference, so unless you explicitly make a copy of that object (in this case creating a new list by using .ToList()) you new object and the loop both point to the same object in memory, that's why called .Clear() empties the list in both places you are looking at it.
A quick fix for you piece of code would be ... { TimeCard = lscheckdetails.ToList(), ... This creates a new copy of the list and lets you clear the one in your loop.

Initialize nested Object from a list

I have a list of integers (Levels). I want to initialize a nested Object of Filter called myFilter as below(Filter is a class with two properties: Value and NextFilter):
var myFilter = new Fliter{
Value = Levels[0],
NextFilter = new Filter{
Value = Levels[1],
NextFilter = new Filter{
Value = Levels[2],
NextFilter = new Filter{
Value = Levels[n],
NextFilter = null
}
}
}
}
Level's count is not static and depends on the input list (I have a multi select list that generates Level)
How can I do that?
This is a classic event for using - the technique of a method that calls itself:
public static Filter CreateFilter(List<int> values) => values.Any() ? new Filter //If the list contains elements, create the filter
{
Value = values.First(), //assign the first item of the values to the value property
NextFilter = CreateFilter(values.Skip(1).ToList()) //Create the rest of the nested object with the rest of the values
} : null; //If there aren't any items left in the list, return null and stop the recursion
You could of course do it in the constructor as well:
public Filter(List<int> values)
{
if (!values.Any()) return;
Value = values.First();
NextFilter = values.Count > 1 ? new Filter(values.Skip(1).ToList()) : null;
}
For more information about recursion, take a look at this: https://www.dotnetperls.com/recursion, for more information on nested classes read through this: https://www.dotnetperls.com/nested-class.
A few more information on recursion:
You can actually achieve everything through recursion - you don't even need loops. That's the reason why in languages like Haskell loops don't exist.
The simplest recursive function is:
public static void EndlessLoop()
{
//Loop body
EndlessLoop();
}
However, even Resharper suggests to convert it to a loop:
Another example, if you want to get the sum of a list you could do:
public static int Sum(List<int> summands) => summands.Count > 0
? summands.First() + Sum(summands.Skip(1).ToList())
: 0;
But those examples aren't useful in C#, as C# isn't a functional programming language, which causes recursion to be slower than loops. Furthermore recursion often causes a StackOverflowException (fitting to this site). If you run the endless loop recursion, it doesn't even take a second till your stack is full.
The reason for this is, that C# adds the address, from which a method got called, to the stack. If a method is called very often (and in 1 second a lot of recursive calls are made) a lot of addresses are added to the stack, so that it overflows.
However I still think, even though those examples aren't useful in c#, that it's quite useful to be able to handle recursion. Recursion is for example the only way to explore a directory structure, for getting for example all files:
public static List<FileInfo> GetAllFiles(DirectoryInfo directory) => directory.GetFiles()
.Concat(directory.GetDirectories().SelectMany(GetAllFiles))
.ToList();
And, as you experienced, it's the only way to fill a nested class from a list properly.
Just make a constructor of Filter, that will get Levels array as a parameter, that will set it's Value as level[0], and init NextFilter = new Filter(level.Skip(1)). Something like that. And it will recursively initialize your object.

Is it possible to instantiate objects with variable names, or access variable names at runtime?

I have quite a few lines of code that create objects and using various parameters with similar object names and constructors. The only thing that is changing is the actual name of the object variable being created, and the name of the objects themselves being passed in. Here is an example of code that matches my current setup:
BackyardObject backyardObject0 = new BackyardObject(cat0, dog0, goat0, piglet0);
BackyardObject backyardObject1 = new BackyardObject(cat1, dog1, goat1, piglet1);
BackyardObject backyardObject2 = new BackyardObject(cat2, dog2, goat2, piglet2);
BackyardObject backyardObject3 = new BackyardObject(cat3, dog3, goat3, piglet3);
BackyardObject backyardObject4 = new BackyardObject(cat4, dog4, goat4, piglet4);
// many many more BackyardObjects being instantiated
As we can see, the names of the object being created match exactly the names of the objects being passed into the constructor. Is it possible to create a loop that would set all this up?
Edit
I think I might have lacked details that were needed to get a correct answer for this question. It isn't a question on "how-to" use a loop, or how to add items to a collection it's more of a question to determine if is it possible to create a "variable name" dynamically inside of a loop, while accessing another variable name dynamically inside of the loop provided the information given above (just made up code on the spot).
// psuedo code for something I'm asking is possible
for( i = 0; i < 10; i++)
{
// create BackyardObject with generic name, while appending "i" to
// variable name, and accessing other object variables
BackyardObject backyardObject'i'
= new backyardObject(cat'i', dog'i', goat'i', piglet'i');
}
While I understand that I could create additional arrays and lists to store objects and then use those, I was just seeing if it was possible to get variable names dynamically. I wasn't sure if it was entirely possible, that's why I asked the question. I know that this is a strange question, but got curious after I seen this Objective-C code:
// Getting an arrayName dynamically based on loop index
for (int i = 0; i < 10; i++)
{
NSString *arrayName = [[NSString alloc] initWithFormat:#"column%d",
i];
NSArray *array = [self valueForKey:arrayName];
}
Objects don't have names... what you're talking about is variables. And rather than having lots of variables with numbers as suffixes, you'd be better using collections - arrays, or lists, or whatever. Then you can do:
// Or use arrays...
List<Cat> cats = new List<Cat>();
cats.Add(new Cat(...)); // Add the cats however you want to set them up
// Ditto dogs, goats etc
List<BackyardObject> backyardObjects = new List<BackyardObject>();
for (int i = 0; i < 10; i++)
{
backyardObjects.Add(new BackyardObject(cats[i], dogs[i],
goats[i], piglets[i]));
}
I assume you're fairly new to C# - I suggest you look at arrays and collections in whatever you're learning from.

List<> collection members changing when they aren't supposed to

I have a List that I have populated in the main method of a console project. I pass this population to a method which is meant to take two members of the population and decompose and recombine them in a way to create two new unique members which will later be added to the population.
However when I manipulate the two original members to create the two new unique members the two original members change with in the initial population (hence altering the initial population).This means that when I go to add the new members I get duplication of entries into the List.
I'm not doing anything overly complicated I think I am just doing something stupid.
Does any one have any insight as to why this is happening ?
Here is the method that gets called to choose to initial two members of the population:
public static List<Chromosome<Gene>> runEpoch(Random rand, List<Chromosome<Gene>> population, SelectionMethod selectionMethod)
{
int populationSize = population.Count;
int selectionCount = (int)Math.Truncate((population.Count * 0.75));
if (selectionMethod == SelectionMethod.Tournament)
{
for (int i = 0; i < selectionCount; i++)
{
Chromosome<Gene> parent = selection.runTournament(rand, population);
Chromosome<Gene> parentTwo = selection.runTournament(rand, population);
//Checks for the presence of incestuous mating. In some cases incestuous mating causes a stack overflow to occur that the program can not recover from
if (parent != parentTwo)
{
//where runGeneOperators calls the crossOver method directly
offSpring = runGeneOperators(rand, parent, parentTwo);
}
else
{
i--;
}
}
}
else
{
//NSGAII
}
//fixPopulation is meant to sort and remove any excess members
return fixPopulation(rand, population, selectionMethod, populationSize); ;
}
And here is the code that is creating the two new unique members :
public List<Chromosome<Gene>> crossOver(Random rand, Chromosome<Gene> parentOne, Chromosome<Gene> parentTwo)
{
List<Chromosome<Gene>> offSpring = new List<Chromosome<Gene>>();
int crossPtOne = rand.Next(0, parentOne.Length);
int crossPtTwo = rand.Next(0, parentTwo.Length);
if ((crossPtOne == 0) && (crossPtTwo == 0))
{
offSpring.Add(parentOne);
offSpring.Add(parentTwo);
return offSpring;
}
else
{
GeneNode<Gene> fragOne = parentOne.Children[crossPtOne];
GeneNode<Gene> fragTwo = parentTwo.Children[crossPtTwo];
crossOverPoint = crossPtOne;
GeneNode<Gene> genotype = performCrossOver(parentOne.Genotype, fragTwo);
success = false;
parentOne.repair(genotype);
offSpring.Add(parentOne);
crossOverPoint = crossPtTwo;
GeneNode<Gene> genotype2 = performCrossOver(parentTwo.Genotype, fragOne);
success = false;
parentTwo.repair(genotype2);
offSpring.Add(parentTwo);
}
return offSpring;
}
private GeneNode<Gene> performCrossOver(GeneNode<Gene> tree, GeneNode<Gene> frag)
{
if (tree != null)
{
if (crossOverPoint > 0)
{
if (!success && tree.Left != null)
{
crossOverPoint--;
tree.Children[0] = performCrossOver(tree.Left, frag);
}
}
if (crossOverPoint > 0)
{
if (!success && tree.Right != null)
{
crossOverPoint--;
tree.Children[1] = performCrossOver(tree.Right, frag);
}
}
}
if (!success)
{
if (crossOverPoint == 0)
{
success = true;
return frag;
}
}
return tree;
}
In C#, objects are reference types, meaning adding something to a collection only adds a reference. If you manipulate a variable with the same reference (in your case, the "original" objects), all references pointing to that object will be changed as well. You need to copy the object somehow to have a different object and manipulate that.
To manipulate your collection and objects within it without altering it, you need to do a deep clone of the collection and it's containing objects, so you can alter list A without altering clone B. If you just wish to change some object inside the list you need to make a deep clone of the object inside the list and then alter the deep clone. This will make sure that your original is not altered.
The IClonable interface should be implemented on your objects to make them clonable.
EDIT: Per Henk's Comments, you should just implement your own copy method without implementing the IClonable interface, as per this article.
Cloning of objects comes in two forms, deep and shallow.
Shallow: This form of cloning creates a new object and then copyies the nonstatic fields of the current object to the new object. If a field is a value type, a bit-by-bit copy of the field is performed. If a field is a reference type, the reference is copied but the referred object is not; therefore, the original object and its clone refer to the same object.
Deep:This creates a new object and clones all data members and fields of the clonable object. This object references X2 where the original will reference X.
Here a post from SO that might assist you in cloning.
Item 1 - Keep your list references private
From a API perspective, an object should never give out the reference to its internal list. This is due to the issue you have just found, whereby some other consumer code can go ahead and modify that list as it sees fit. Every reference of that list, as shared around, including the 'owning' object, has their list updated. This is usually not what was intended.
Further, the responsibility is not on the consumer to make its own copy. The responsibility is on the owner of the list to keep its version private.
It's easy enough to create a new List
private List<Gene> initialGeneList;
public List<Gene> InitialGenes
{
get { return new List<Gene>(initialGeneList); } // return a new list
}
This advice also includes passing your own list to another class/method.
Item 2 - Consider using an immutable object
Without going too much into reference vs value types, what this means is treat the object like a value type, and whenever you have an operation, return a new object instead of mutating the original.
Real value types in .net, like integers and DateTimes have the semantic of copy on assignment. e.g. When you assign, a copy is created.
Reference types can also behave like value types, and strings are good example. Many people make the following mistake, because they fail to realize the value-type semantics of System.String.
string s = "Hello World";
s.Substring(0,5); // s unchanged. still 'Hello World'
s = s.Substring(0,5); // s changed. now 'Hello'
The string method Substring() doesn't mutate the original object, but instead returns a new one containing the result of the operation.
Where appropriate, the use of immutable objects can make the program a lot easier to understand and can reduce defects considerably.
When implementing immutable objects, one caveat is the likely need to implement value equality for comparing two objects (by overriding Equals). Objects are reference types, and by default two objects are equal if their references are equal.
In this snippet, the values of the objects are equal, but they are different objects.
string s1 = "Hello";
string s2 = string.Concat("He","l", "lo");
bool equal = s1.Equals(s2);
bool referenceEqual = object.ReferenceEquals(s1, s2);
// s1 == s2 => True, Reference Equal => False
Console.Write("s1 == s2 => {0}, Reference Equal => {1}", equal, referenceEqual);

fastest way to copy from a collection of one type to another c#

I get a collection of business entities (table data) equal to around 49000 . I am trying to copy certain values from this collection to anothr collection using Add method and manually adding values to its properties
For example
//Returns arounf 49000 VendorProduct collection this refers to the data in the table VendorProduct
List<VendorProduct> productList = GetMultiple(req);
foreach (VendorProduct item in productList)
{
VendorSearchProductWrapper wrapperItem = new VendorSearchProductWrapper();
IEnumerable<ClientProductPreference> prodPrefClient = item.ClientProductPreferences.Where(e => (e.VendorProductId.Equals(item.Id)
&& (e.AccountId.Equals(SuiteUser.Current.AccountId))));
if (prodPrefClient.Count() == 1)
{
foreach (ClientProductPreference it in prodPrefClient)
{
wrapperItem.ClientProdID = it.Id;
wrapperItem.ClientProductDescription = it.Description;
wrapperItem.MarkupPct = it.MarkUpPct;
wrapperItem.SalesPrice = it.SalesPrice;
}
}
wrapperItem.Code = item.Code;
wrapperItem.ListPrice = item.ListPrice;
wrapperItem.Id = item.Id;
wrapperItem.DiscountPct = ValueParser.SafeDecimal(item.Discount, null);
wrapperItem.Discountgroup = item.DiscountGroup.DiscountGroup;
wrapperItem.Description = item.Description;
wrapperItem.Unit = item.Unit;
wrapperItem.ClientName = item.Account.ClientName;
products.Add(wrapperItem);
}
To copy all of these 49000 records it takes lot of time .Infact for 5 mins only 100-200 records get added to the List.
I need to copy these values faster say in about 1 Minute.
Thanks In Advance
Francis P.
IEnumerable<ClientProductPreference> prodPrefClient = item.ClientProductPreferences.Where(e => (e.VendorProductId.Equals(item.Id)
&& (e.AccountId.Equals(SuiteUser.Current.AccountId))));
if (prodPrefClient.Count() == 1)
{
foreach (ClientProductPreference it in prodPrefClient)
{
There is so much wrong in this code.
Try retrieving this value as SingleOrDefault and then checking for NULL. The way you are doing it, is iterating TWICE over same data. First to get count, second to iterate in foreach (that is useless too, because you know collection have only 1 item and creating whole one iterator for one item is crazy)
Is it possible to use some kind of Dictionary?
Check for lazy loading. When you know you will need the data (and I can see you need them), you should eager-load them to reduce ammount of database calls.
Best way to do this is to make dedicated SQL (or LINQ, because its quite simple) query, that would execute completly on DB. This will be fastest solution possible.
This should be happening faster, even with code you have. Are you sure there aren't any lengthy operations in there? Eg. lazy loading, other data calls, ...
Even small ones can have a big impact on a 49000 iteration
I agree with #Bertvan. The iteration should not take that much time (even if the records are 49K).
I would suggest considering the following lines ( which could be creating problem):
if (prodPrefClient.Count() == 1)
Not sure what are you trying to achive here, But this call is iterating a lazy collection. Please consider if you need this check.
wrapperItem.DiscountPct = ValueParser.SafeDecimal(item.Discount, null);
Int/double comparers do eat some processing time. You should also check the total time taken by the operation by removing this line of code.
Hope this would help.

Categories