The code below doing a simple thing. manipulate the API's raw data and bind it to the view model's property.
I fetch data from a web service and save it to a object named calljsonObject. This is the raw data. Then I do a foreach loop to select the selected data from the raw data, and save it to a object named tmpList, and at the last line, bind tmpList to the view model property named docList
SingleBoardListRawData calljsonObject = JsonConvert.DeserializeObject<SingleBoardListRawData>(callbackjsonstring);
ObservableCollection<SinglePostViewModel> tmpList = new ObservableCollection<SinglePostViewModel>();
SinglePostViewModel tmpPost = new SinglePostViewModel();
foreach (List<object> item in calljsonObject.data)
{
tmpPost.doc_id = (long)item[0];
tmpPost.doc_title = (string)item[1];
tmpPost.doc_author = (string)item[2];
tmpPost.repliesCount = (long)item[4];
tmpPost.doc_post_date = DateTime.Parse((string)item[5]);
tmpPost.OnTop = (long)item[6];
tmpPost.CoolPost = (long)item[7];
tmpPost.doc_update_date = DateTime.Parse((string)item[9]);
tmpPost.PicInDoc = (long)item[10];
tmpPost.reply_user_name = (string)item[11];
tmpPost.doc_brief = (string)item[13];
tmpList.Add(tmpPost);
}
this.docList = tmpList;
But the result is the tmpList full of the same data, which is the result of the last foreach manipulation. But I thought that I've re-assigned the tmpPost value, it seems every Add method will take place the previous one.
I don't want to new a object everytime I do a foreach loop, I think it cost a lot,
My Question is :
1. Why?
2. How to solve it?
You're adding a reference to the same object on each iteration, and overwriting the data within that object on each iteration. You need to create a new object on each iteration. Move this line:
SinglePostViewModel tmpPost = new SinglePostViewModel();
into the loop.
foreach (List<object> item in calljsonObject.data)
{
SinglePostViewModel tmpPost = new SinglePostViewModel();
tmpPost.doc_id = (long)item[0];
...
}
I don't want to new a object everytime I do a foreach loop, I think it cost a lot,
How do you expect to maintain the different values if you don't create that many objects? Where do you expect the data to live? And what evidence do you have for your performance concern?
It's very important that you understand how reference types and value types work. Please read my article on this topic and think about how that applies in your situation.
If we assume that SinglePostViewModel, then you are adding the same reference multiple times in the list. There is only one instance of the SinglePostViewModel - you are simply over-writing it each time, hence the fail.
I don't want to new a object everytime I do a foreach loop, I think it cost a lot
Unless you are talking 10s of millions of records, your concerns are very unlikely to be justified. The data needs to go somewhere, after all.
My Question is : 1. Why? 2. How to solve it?
because you only have one object that you keep over-writing
don't do that
Related
I have a file with 2 columns and multiple rows. 1st column is ID, 2nd column is Name. I would like to display a Dropdown where I will show only all the names from this file.
I will only iterate through the collection. So what is the better approach? Is creating the objects more readable for other developers? Or maybe creating new objects is too slow and it's not worth.
while (!reader.EndOfStream)
{
var row = reader.ReadLine();
var values = row.Split(' ');
list.Add(new Object { Id = int.Parse(values[0]), Name = values[1] });
}
or
while (!reader.EndOfStream)
{
var row = reader.ReadLine();
var values = row.Split(' ');
dict.Add(int.Parse(values[0]), values[1]);
}
Do I lose the speed in the case if I will create new objects?
You create new objects, so to speak, also while adding to the Dictionary<T>, you create new Key-Value pair of the desired type.
As you already mentioned in your question, the decision is made on primary
expected access pattern
performance considerations, which are the function also of access pattern per se.
If you need read-only array to iterate over, use List<T> (even better if the size of the data is known upfront use Type[] data, and just read it where you need it).
If you need key-wise access to your data, use Dictionary<T>.
If you want to only iterate objects, then use List. No need to use Dictionary class at all.
In a for loop, instead of declaring a new object and add it to my List, I just update the value of the old object and every time keep adding this old object to my List, why after a few loops all List elements become the same
foreach (vg_ts_VesselCashflow_CashFlow_Entity item in cashflow)
{
var result = new VslMonthlyCashflow_Record();
result.CapitalCost = item.CapitalCost;
result.CharterRevenue = item.CharterRevenue;
result.Date = item.Period;
result.DryDock = item.DryDock;
resultList.Add(result);
}
if (resultList != null)
return resultList;
//Compared with this:
var result = new VslMonthlyCashflow_Record();
foreach (vg_ts_VesselCashflow_CashFlow_Entity item in cashflow)
{
result.CapitalCost = item.CapitalCost;
result.CharterRevenue = item.CharterRevenue;
result.Date = item.Period;
result.DryDock = item.DryDock;
resultList.Add(result);
}
I expect my resultList to be updated but if I keep using the old object, when it loops 123 times, all elements in the List will be the same.
List#Add doesn't copy the object or anything like that, it just keeps a reference to the object you passed to it. In the second snippet, you keep adding the same object to the list multiple times. Each modification you perform on the object is visible through all the references pointing to it, including the local result variable and all the elements of the list.
I am new to using ViewModels, I have a new list here and am adding items to it by looping though a database table. The issue is that all the records that come back are identical using the same record over and over. What could be the issue and is this a good way to accomplish filling with data and Passing a ViewModel or is there a better way? Right now it returns about 500 records with the same data.
public class DimCustomersController : Controller
{
private AdventureWorks_MBDEV_DW2008Entities db = new AdventureWorks_MBDEV_DW2008Entities();
public ActionResult CustomersIndexVM()
{
List<DimCustomersIndexViewModel> CustomerList = new List<DimCustomersIndexViewModel>();
DimCustomersIndexViewModel CustomerItem = new DimCustomersIndexViewModel();
foreach (var m in db.DimCustomers.ToList())// cold do for loop up to count
{
CustomerItem.Title = m.Title;
CustomerItem.FirstName = m.FirstName;
CustomerItem.MiddleName = m.MiddleName;
CustomerItem.LastName = m.LastName;
CustomerItem.BirthDate = m.BirthDate;
CustomerItem.MaritalStatus = m.MaritalStatus;
CustomerItem.Suffix = m.Suffix;
CustomerItem.Gender = m.Gender;
CustomerItem.EmailAddress = m.EmailAddress;
CustomerItem.AddressLine1 = m.AddressLine1;
CustomerItem.AddressLine2 = m.AddressLine2;
CustomerItem.Phone = m.Phone;
//other columns go here
CustomerList.Add(CustomerItem);
}
return View("CustomersIndexVM", CustomerList);
}
This line needs to be inside the loop:
DimCustomersIndexViewModel CustomerItem = new DimCustomersIndexViewModel();
The reason is that you want a new view model for each customer, but instead you are currently creating only one view model and changing its properties. When you add it to the list, you are not adding a copy; you are adding the same view model you already added.
This code would work if DimCustomersIndexViewModel was a struct, because structs are just a bag of values that have no inherent identity and they are copied rather than referenced. (Technical comparison.) But it's a class (as it should be), with a unique identity, so you're adding a reference to the single view model into the list over and over. Customerlist[0] and CustomerList[1] and all the other items point to the same DimCustomersIndexViewModel object instance, whose properties are then overwritten and left equal to the very last customer.
By moving this line inside the loop, you are creating a separate DimCustomersIndexViewModel for each customer, each with its own set of properties, and CustomerList contains references to many different DimCustomersIndexViewModel object instances.
Once you have solid experience with this concept, a future step could be to use AutoMapper so that you don't have to maintain a list of all properties in your code here.
The problem is you add the same reference object during each iteration of your loop. That object never changes (you never new it up again), but you change the properties on the object. Then you add that object over and over. You need to new up that object each iteration of the loop.
I have same type of 2 object lists and first item of first list should be added specific times. Also every cycle i should increase the datetime value with AddSeconds(). This is my code below:
List<Logs> logList = new List<Logs>();
List<Logs> chartList = new List<Logs>();
for (int i = 0; i < specificValue; i++)
{
chartList.Add(logList[0]);
logList[0].Date = logList[0].Date.AddSeconds(1);
}
My purpose is creating chart data with change log so i have to add same value to chartList with specific time with incremental date value. But my problem this code creates a chartList with all the same value of the last loglist item. I found some solutions with using foreach but in that situation i have to use for loop.
When you call chartList.Add(logList[0]), you are adding a reference to the object, not creating a new object.
So on the next line, when you add 1 second to the object, you also add that 1 second to the object in chartList. In fact, all the objects in chartList are references to the exact same object.
You need to create a copy of the object when you add it to chartList.
var users = new List<User>();
User user = new User();
foreach (var element in elements)
{
try
{
user.Id = element.Attribute("Id").Value;
user.Reputation = element.Attribute("Reputation").Value;
user.CreationDate = DateTime.Parse(element.Attribute("CreationDate").Value);
user.DisplayName = element.Attribute("DisplayName").Value;
user.LastAccessDate = Convert.ToDateTime(element.Attribute("LastAccessDate").Value);
user.WebsiteUrl = element.Attribute("WebsiteUrl").Value;
user.Location = element.Attribute("Location").Value;
user.Age = Convert.ToInt32(element.Attribute("Age").Value);
user.AboutMe = element.Attribute("AboutMe").Value;
user.Views = element.Attribute("Views").Value.ToInt32();
user.UpVotes = element.Attribute("UpVotes").Value.ToInt32();
user.DownVotes = element.Attribute("DownVotes").Value.ToInt32();
users.Add(user);
}
catch (NullReferenceException)
{
// do nothing
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine(e.Message);
}
}
The expression User user = new User(); is used outside the foreach loop. My intention is to avoid instantiating new User object, if a loop runs 10000000 time (suppose), what if I just instantiate a single time and on each loop I just change its contents variable and adding to the a list. But what happen... I although get all different users, but in my final list there is only one user (the first user) and it has as many copies as loop runs...
The problem can be solved the taking the express User user = new User(); in foreach loop, but I'm not convinced by myself, why it need to instantiate a new object on each loop ?..... Please clear me
When you add it to the list, you adding a reference to the object you are creating and the list is keeping it for future use.
By putting the "new" inside the loop, you are adding a new object each time. You get a performance hit because you are allocating memory for it. There are ways to mitigate this so the hit is somewhere else (i.e. a pool/list of them created when your program starts up that you use when the program runs), but you must allocate them somewhere.
By not putting a "new" inside the loop, every element of the list points back to the same single object you created.
If you want to keep different information for each object, it must be stored somewhere.
This concept is NOT just for C#.
Reference types and value types are different.This is because every time in the foreach loop you just changing one User and add it to your list.
Value types: A data type is a value type if it holds the data within its own memory allocation.
Reference types: A reference type contains a pointer to another memory location that holds the data.
Check this for more details: http://msdn.microsoft.com/en-us/library/t63sy5hs.aspx
As the others said you should move your User definition inside of your foreach loop:
foreach (var element in elements)
{
User user = new User();
...
}