Reputation: 19
We are performing a migration and we are supposed to log the final report for the same. To achieve the same, I have a class which has multiple properties that holds List of Ids(string) such as All the Ids involved in Migration, All the Ids which are Migrated etc. While doing the same what I came across was that my previous assignment was getting updated when I performed a RemoveAll on a variable that holds all the List of Ids, to better understand, PFB an example:
public class Report {
public IdsInfo AllIds {get; set;}
public IdsInfo MigratedIds {get; set;}
public IdsInfo FailedIds {get; set;}
}
public class IdsInfo {
public int Count{get; set;}
public List<string> Ids {get; set;}
public IdsInfo(List<string> ids) {
Count = ids.Count;
Ids = ids;
}
}
//An example of the Method that does the Migration
public Report Migrate(string criteria) {
Report report = new Report();
//Assume below is a DB Call
List<string> allIds = db.getData(criteria).Select(x=>x.id).toList();//Assume Count = 1000
report.AllIds = new IdsInfo(allIds);
//Assume below is a DB Call to check whether Ids are already migrated
List<string> migratedIds = db.getData(x=>allIds.contains(x)).Select(x=>x.id).toList();//Assume Count = 300
allIds.RemoveAll(x=>migratedIds.contains(x));
report.MigratedIds = new IdsInfo(migratedIds);
return report;
}
What I was expecting =>
Report.AllIds.Count = 1000
Report.AllIds.Ids= {1000}//List
Report.MigratedIds.Count = 300
Report.AllIds.Ids= {300}//List
What actually happened =>
Report.AllIds.Count = 1000
Report.AllIds.Ids= {300}//List
Report.MigratedIds.Count = 300
Report.AllIds.Ids= {300}//List
I understand that Pass By Reference is what is causing this behavior but I would like to understand the reason behind this. My questions are as follows:
Upvotes: 2
Views: 50
Reputation: 141835
List<T>
is a reference type, so passing allIds
into IdsInfo
constructor will make report.AllIds
and allIds
point to the same one instance of the list, resulting in all modifying operations (like RemoveAll
) being observed in both places.
If you want to modify allIds
later without affecting the instance inside the report
just create a copy of the list with ToList
(note that it shallow copies the contents of the list, which is fine for string
's but for other reference types can lead to similar behavior for elements of the list):
report.AllIds = new IdsInfo(allIds.ToList());
Upvotes: 2
Reputation: 37000
Only because you create a new object of your type doesn´t mean you´re also creating a new list. In this line you just add a new reference to the same list.
report.AllIds = new IdsInfo(allIds);
So both report.AllIds
and allIds
will now reference the exact same list. Doing something on that list - e.g. by calling RemoveAll
- is then reflected in all references to that list. You can read more about reference-types in the docs.
So what you need is to copy the list:
report.AllIds = new IdsInfo(allIds.ToList());
ALternativly you can also create the copy within the constructor of IdsInfo
, as Guru mentioned in his answer.
ToList
will really create a new list from the input-one, even if the input already is a list itself.
Upvotes: 1