What's happening to my lists? - c#

why does
List<Object> objectList; = some objects
List<Object> getList()
{
return objectList; //or return new List<Object>(objectList);
}
return a list with all items referenced to the original list's items?
Thanks.

In the first case you just return a reference to the list.
In the second case (new List<Object>(list)) the objects are not copied: only the references are copied! You have to clone each item in the collection to return a deep copy of the list.
EDIT:
Iterate through your whole list and create a copy of each of your objects and put them into a new list.
See this for creating deep copies of custom objects. I would suggest not to use the interface ICloneable. Make some research to learn why. :)

It is a reference
You're returning a reference to ObjectList. : )
AboutTheConstructor:
From MSDN: List<(Of <(T>)>)(IEnumerable<(Of <(T>)>)) Initializes a new instance of the List<(Of <(T>)>) class that contains elements copied from the specified collection and has sufficient capacity to accommodate the number of elements copied.

Basically, because List<> and Object are reference types. If you read up on reference/value types in C# here: http://msdn.microsoft.com/en-us/library/3ewxz6et(v=VS.100).aspx it should make sense to you.

if you are using a custom object, Implement the ICloneable interface on the object.
Then, when you are returning your list, clone the objects into a new list. When you pass your old list into the constructor of your new list, you are passing references to all the objects in the original list, unless they are value type (string, int, etc)

Related

Modifying a List of Objects through a List of Wrappers

My goal here is to retrieve a List from a class instance and then modify the objects within that list, however I do not know how the objects will be affected and need some valuable knowledge.
Let's say we have two cases:
Create a new List which gets the list from the class instance:
List<SomeType> ItemsList {get { return classInstance.ItemsList;} set {...
Create a new List of wrappers, each item in the list contains the original object with some additional properties:
List<SomeType> itemsList = classInstance.ItemsList;
List<Wrapper> wrapperList = itemsList.Select(x => new Wrapper {Item = x, IsSelected = ...
My question is, if I were to modify the objects in ItemsList and wrapperList, would the changes be reflected in the classInstance?
I will be using multiple ObservableCollections in multiple DataGrids, I have used Lists in this example simplify the situation but I'm sure the result would be the same.
Edit:
I realize the Title does not describe my problem well, I encourage anyone to help me improve it.
My question is, if I were to modify the objects in ItemsList and wrapperList, would the changes be reflected in the classInstance?
Yes, unless you create a copy SomeType and add the copy to the new collection.
The following creates a new Wrapper objects for each SomeType object...
List<Wrapper> wrapperList = itemsList.Select(x => new Wrapper {Item = x } );
...but each Wrapper object still has a reference (though the Item property) to the same SomeType object that's in classInstance.ItemsList.
When the properties of SomeType (mutable reference type) instances are changed via Wrapper then the objects in the original list change, because they are the same objects.
When the list holding Wrapper objects changes (you move, add or remove objects) the the original list is not affected.
When original list is changed (you move, add or remove objects) the list containing Wrappers is not affected.
To synchronise the collections, you should use ObservableCollection for original list and implement collection (probably also ObservableCollection-derived) for Wrappers that auto-updates itself when source collection changes.

A change in one object changes the second one too

I'm creating two objects and assign them with a data from IsolatedStorage.
But when I change one object the second one changes too. ( I think the problem may be the pointers are the same, but I can't solve it. )
private ArrayOfClsSimpleData lstUsers;
private ArrayOfClsSimpleData tmpLstUsers;
in class' globals
tmpLstUsers = IsolatedStorageHelper.GetObject<ArrayOfClsSimpleData>("users");
lstUsers = IsolatedStorageHelper.GetObject<ArrayOfClsSimpleData>("users");
The first status of the arrays:
Debug.Write(lstUsers.Count)
Output: 2
Debug.Write(tmpLstUsers.Count)
Output: 2
The counts are the same as expected. But, after I add an item to one list, the other list gets updated too and the counts are still same.
lstUsers.Add(new ArrayOfClsSimpleData());
Debug.Write(lstUsers.Count)
Output: 3
Debug.Write(tmpLstUsers.Count)
Output: 3
EDIT : IsolatedStorageHelper class is something to help to get objects, save object etc. that I do use for simplifying things, so just think it as getting objects from IsolatedStorage.
it is implemented like this:
public static T GetObject<T>(string key)
{
if (IsolatedStorageSettings.ApplicationSettings.Contains(key))
{
return (T)IsolatedStorageSettings.ApplicationSettings[key]; // return the object
}
return default(T); // if key doesn't exists , return default object
}
So it just gets it from IsolatedStorage.
If you don't know isolated storage you can see it from here
So, how can I fix the code so that I can change one without changing the other?
So, basically lstUsers and tmpLstUsers are references to the same object. All you have to do is to create a new one and copy content from the original. If you need a quick solution, then you can do it like this (code below). I just guess that ArrayOfClsSimpleData is some kind of array.
lstUsers = IsolatedStorageHelper.GetObject<ArrayOfClsSimpleData>("myKey");
tmpLstUsers = new ArrayOfClsSimpleData();
foreach (object user in lstUsers) // I don't know the type of objects in ArrayOfClsSimpleData, so I wrote 'object', but you should use the actual type
tmpLstUsers.Add(user);
The problem is that IsolatedStorage is just returning two pointers to the same data. So unless you copy the data, all changes will ultimately be to the same underlying data.
Think of it as two copies of your home address. Anything you change on your home affects all copies of your address since it is just an address and not the home itself.
What you will want to do is clone your object. Built in collections have clone or copy methods built in to do shallow copies, or if you built something yourself you will need to implement it yourself
The easiest way is to implement the IClonable interface and to use the clone method to achieve your copying.
https://msdn.microsoft.com/en-us/library/System.ICloneable.aspx
This basically involves going through and calling member wise clone for each complex object (which will copy all value types for you)
https://msdn.microsoft.com/en-us/library/system.object.memberwiseclone.aspx
I don't think cloning is necessary. Just create a new list instead of operating on the same instance. You can do that by calling ToList() on the returned instance:
lstUsers = IsolatedStorageHelper.GetObject<ArrayOfClsSimpleData>("myKey").ToList();
Can't you use the Clone() method of IClonable while fetching the object? looks like both list objects are getting same reference objects.

Possible to Create an ArrayList of Objects in C#/.NET?

I'm trying to create an ArrayList of objects in C#. What I've tried so far is:
class POObject
{
public List<string> staticCustInfo;
public List<List<string>> itemCollection;
public int testInt;
}
POObject myObject = new POObject();
List<POObject> BatchList = new List<POObject>();
This is fine, except when I try to add an object to this list using:
BatchList.Add(item);
It gives me errors saying it can't find a corresponding method to add to the list. Any ideas? Thanks for the help.
What are you trying to store in your list? You declare a BatchList as containing POObjects, but then you're trying to add item, which is of what type? If you're adding things to this list that are not POObjects, you can declare it as either a non-generic list:
List BatchList = new List();
or as a generic list of objects:
List<object> BatchList = new List<object>();
But I suspect the real problem may be that you're trying to add the wrong type of item to your collection. Can you show us how you get your item variable?
If you declare that BatchList is of type List<POObject>, then that means the List can only contain objects of type POObject. That's what the type parameter is for. You cannot add strings, ints, or any other arbitrary objects to this list, only POObjects (and descendents of POObject).
If you take a look at the intellisense, you will see that Add's signature is this:
void Add(POObject item)
If you really want a list that contains arbitrary items, then use an ArrayList, which is not a generic type and can accept any type of object. However since C# is such a strongly typed language, wanting arbitrary lists like this is usually a code smell.

C# - Using a copy of Session-stored variables instead of reference

I have an asp:ImageButton with OnClick="Btn_OnClick".
In Btn_OnClick I have this line:
DataTable dtTable = (DataTable)Session["someSessionKey"]
and dtTable is altered in the function.
I've noticed that if the button's clicked more than once, the dtTable I take from the session contains the altered table, probably meaning dtTable is not a copy but a reference of the session variable.
How can I alter a copy of Session["someSessionKey"], and not the actual value?
Thanks!
DataTable dtTable = ((DataTable)Session["someSessionKey"]).Copy();
If the object in the Session implements ICloneable, you can use var myCopy = mySessionObject.Clone();. You would then have to read the documentation of the object to see what it exactly does.
This is because there is no generic solution to cloning. Mostly, objects have other non-scalar objects as properties, so it always depends on the scenario if you need to clone those child objects too.
When you author your own class, you can always call the protected MemberwiseClone() method that is defined in System.Object to get a flat clone. You can then choose if you want to add some additional cloning logic and if you want to make it available for callers outside.
If the object in the Session is a List<T> or an IEnumerable<T>, you can do var myCopiedList = new List<T>(myListInSession);. Keep in mind that the individual objects in that list are not cloned in that case. You'd have to foreach through them and clone each object manually. It's similar with the DataTable. You might need to manually create a new instance and copy the content over.
To manually clone an object that doesn't support any public method to do so, you'd have to create a new instance of it. Then you'd assign every property from the original object to the cloned object manually. That can be not only tedious, often it also won't work because there are protected members that you can't access easily.
Finally, you can resort to reflection or other tricks to try to clone an object. A quick google search gave me that result: C# Object clone wars.

Unable to cast lists with Reflection in C#

I am having a lot of trouble with Reflection in C# at the moment. The app I am writing allows the user to modify the attributes of certain objects using a config file. I want to be able to save the object model (users project) to XML. The function below is called in the middle of a foreach loop, looping through a list of objects that contain all the other objects in the project within them. The idea is, that it works recursively to translate the object model into XML.
Dont worry about the call to "Unreal" that just modifes the name of the objects slightly if they contain certain words.
private void ReflectToXML(object anObject, XmlElement parentElement)
{
Type aType = anObject.GetType();
XmlElement anXmlElement = m_xml.CreateElement(Unreal(aType.Name));
parentElement.AppendChild(anXmlElement);
PropertyInfo[] pinfos = aType.GetProperties();
//loop through this objects public attributes
foreach (PropertyInfo aInfo in pinfos)
{
//if the attribute is a list
Type propertyType = aInfo.PropertyType;
if ((propertyType.IsGenericType)&&(propertyType.GetGenericTypeDefinition() == typeof(List<>)))
{
List<object> listObjects = (aInfo.GetValue(anObject,null) as List<object>);
foreach (object aListObject in listObjects)
{
ReflectToXML(aListObject, anXmlElement);
}
}
//attribute is not a list
else
anXmlElement.SetAttribute(aInfo.Name, "");
}
}
If an object attributes are just strings then it should be writing them out as string attributes in the XML. If an objects attributes are lists, then it should recursively call "ReflectToXML" passing itself in as a parameter, thereby creating the nested structure I require that nicely reflect the object model in memory.
The problem I have is with the line
List<object> listObjects = (aInfo.GetValue(anObject,null) as List<object>);
The cast doesn't work and it just returns null.
While debugging I changed the line to
object temp = aInfo.GetValue(anObject,null);
slapped a breakpoint on it to see what "GetValue" was returning. It returns a "Generic list of objects" Surely I should be able to cast that? The annoying thing is that temp becomes a generic list of objects but because i declared temp as a singular object, I can't loop through it because it has no Enumerator.
How can I loop through a list of objects when I only have it as a propertyInfo of a class?
I know at this point I will only be saving a list of empty strings out anyway, but thats fine. I would be happy to see the structure save out for now.
Thanks in advance
I'm assuming that the actual value is not a List<object> but is something like a List<string> or List<int> or some other type that isn't exactly object?
If so, then the reason that the cast is failing is because generic classes are neither co- nor contravariant.
In C# 4.0, however, you will be able to make the foreach loop work by casting to IEnumerable<object> because interfaces can be co/contravariant.
(Much) more information here: http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx
Edit:
Thinking about it, you don't need generic variance here. List<T> implements the non-generic IEnumerable. This is all you need for the foreach loop to operate, and you only need the elements as type object so just cast it to an IEnumerable instead of List<object> and everything should work fine.
Generics and reflection don't mix well, especially for lists. I would strongly suggest using (non-generic) IList, or if that fails (some generic lists don't implement it), IEnumerable (since IEnumerable<T> : IEnumerable) and invoke the Add manually.
I feel your pain, really (I maintain an OSS serialization API).
In C# 3, you can't cast Lists<T> to other types of Lists<T> even when casting to something like List<Object>. It's explicitly not allowed even when casting to List.
In 4.0 variance changes a little with interfaces with the addition of the in and out keywords.
Here is a link to Eric Lippert explaining how and why this is the case.
http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx
Couldn't you just cast them to objects with OfType()?
List<object> listObjects = anObject.OfType<object>().ToList();
Update:
If the requirement wasn't for a List<object>, this would also work and involve no duplication of the list items:
var enumerableObjects = anObject.OfType<object>();

Categories