list Remove method does not remove list item - c#

I have an ASP.Net web app written in C# and it has a List<Employee> object called AffectedEmployees, where Employee is a class I've written. AffectedEmployees is also a property of another class I've written called LitHoldDetails, but I don't think that's pertinent; I just include the information in case I'm wrong, which has, of course, never happened. ;) Anyway, I need to remove a particular Employee object from AffectedEmployees. I first just did a Remove() (Add() works, BTW), but the Employee in question was not removed from AffectedEmployees. So I checked to make sure it was really there by doing a call to Contains(). Contains() doesn't find the Employee in AffectedEmployees, but if I examine the list and compare it to the Employee in the debugger, the Employee is indeed in Affected Employees. I've compared the two with a fine tooth comb and I see no difference in the data. What am I doing wrong? Here's my code:
Employee emp = new Employee().FindByLogin(item.Key.ToString());
if (CurrentLitHoldDetails.AffectedEmployees.Contains(emp))
CurrentLitHoldDetails.AffectedEmployees.Remove(emp);
Note: FindByLogin() initiates emp and fills it with data. item.Key.ToString() is coming from a Hashtable that contains all the Employees I need to remove

You're creating a new Employee object that couldn't possibly exist in the list yet, then trying to remove it. So it doesn't find that exact object (even if the contents of the new Employee object match some other Employee object in the list) and doesn't remove it. If you place a breakpoint on the line with Remove on it, you'll see it doesn't get hit.
Try using RemoveAll to remove all elements matching some condition. (I'm doing a bit of guessing here since I don't know what your classes look like exactly.)
CurrentLitHoldDetails.AffectedEmployees.RemoveAll(x => x.Login == item.Key.ToString());

Remove and Contains test for the presence of the exact object you're trying to remove, not just one with identical data. So when you create a new Employee object, it's looking for that exact object. It doesn't find it, because that exact object has never been added to AffectedEmployees.
This is called "reference equality" and is the default behavior. If you want Remove and Contains to determine equality based on property values, you will need to override the Equals and GetHashCode methods on your Employee object. (See here for Microsoft's documentation on Object.GetHashCode().)

Related

Checking class types in a list box

I am building a GUI where I have two classes, Student and Graduate. Graduate is a child class of Student. The user will fill out forms to add the student to the list. In order to add said information to a view form I need to check what the selected item's type is. I have tried
if(view_list.selectedItem.GetType == Student)
but it doesn't work. How would I be able to check the type of the selected item?
In short, you'll need to compare XXX.GetType() with typeof(YOUR_TYPE).
As for the reason, if you have ever stared at the IntelliCode tips provided by Visual Studio, you should see that the function object.GetType() returns Type
However, Student or any other primitive types like int, string, etc. has type of class
You can't compare two values with different types (in this case the left hand side is of Type but the right hand side is of class)
What typeof() does is to get the Type of a class.
There are a couple of problems.
The GetType which you are trying to use is actually a method, not a property, so you need to call it as GetType().
The next is that you need to use typeof(Student) to compare.
And if view_list is a Winforms ListView, then SelectedItem should start with uppercase S
So then you code should be.
if (view_list.SelectedItem.GetType() == typeof(Student))
{
//Do your stuff
}

Reflection: Get properties/fields from individual elements of a List<>

I'm sorry in advance for the mess you're about to read, because I'm not 100% sure what I'm searching for.
I have created an entire UI system that automatically grabs a list of properties from various scripts/components on GameObjects (Unity) and creates a fitting UI input variant for them (for example, float gets a single line, Vector3 gets 3 lines, color gets something else etc.).
What goes into UI input fields creation is a Component (that we want to look into), while individual created UI inputs store this Component and Property Name. So when input changes in one of input fields, it does SetValue on Property of a Component. Now I have also created a variant where we peak into a Class of a property and basically list Property's Properties, so the UI input stores Component, Property Name, and subProperty's Name and modifies properties as such. All this works well.
So, now I hit a brick wall with Lists. I would like to treat individual elements of a list as properties so that I could pass them into my preexisting UI scheme.
tl;dr Does List<> treat it's individual elements as Properties, Fields or does it depend on the situation? How do I get these properties, fields or their respective names from this list in order to use them with my mess of an UI system? 0 work for me means treating individual elements of List as properties.
----------------------------
EDIT----------------------------
Again I am sorry for this mess of a question. It is a mixture of confused theory and description of an existing situation that I am trying to shoehorn into my already existing project (which is a bit too over the place to be easily reduced to an example).
If anyone grasped what I was asking for, the single easiest solution was to create a property which prior to listing was equated to an element of a list.
Example looks something like this:
public List<MyCustomClass> myCustomList;
[Listable("ForEdit")]
public myCustomClass myCustomPropertyForEdit
{
get;
set;
}
And before withdrawing properties of myCustomPropertyForEdit's class (myCustomClass) I would simply do:
myCustomPropertyForEdit = myCustomList[0]; //or whatever index is in question
Then later on I would do reflection on "myCustomPropertyForEdit" instead of myCustomList. I highly doubt this will ever help anyone because it touches more onto how I built methods for listing properties into my UI, but there it is just in case.
List stores references to objects, by providing an index you get a standard object reference, which you can proceed to query using reflection (do not do it against the list itself as you will get methods of the List class, and notthing related to what the list contains)
take for example:
public Class Tree
{
public int branches;
public Tree(int branch)
{
branches=branch;
}
}
List<Tree> trees = new List<Tree>();
trees.Add(new Tree(3));
now my list has one element at index 0;
so i can do trees[0].branches;
to access the fields/props of an element in trees.
list is not an array, it holds the actual item, allowing you to reference, not just the object but also its own unique variables. as long as they are public in the class.

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.

Listbox contains does not find item, cannot remove an item

I add items to a list box like so:
foreach(myObject object in ListOfObjects)
{
mylistbox1.add(object);
}
foreach(myObject object in ListOfObjectsTwo)
{
mylistbox2.add(object);
}
Further on, I want to remove a couple of items given a specific condition. This is what I do:
foreach(myObject object in ListOfObjects3)
{
mylistbox1.items.remove(object);
mylistbox2.items.remove(object);
}
This only seems to work for mylistbox1 but not mylistbox2. When I debug, I can see that the item is there and that it has the exact same properties as the one I'm trying to remove. When I try to check if the listbox contains the item im trying to remove, it returns false.
I can't seem to make sense of it.
I c# all lists operation that are in some way required to compare objects use Equals or GetHashCode methods. In your case Equals and the default implementation wont check properties values it will only verify if the passed as argument object is in list so consider if you have the same instance in your list or just two different instances that happened to have the same properties. (the helpful VS option is make object id it will mark instance with a number)
If this is the case then you should consider overriding Equals method or find the instance that you want to delete with linq for example and pas that object to Remove method.

How to get a unique object reference as a string for items in a list

I'm working on a .NET phone application where I have a list of items in memory that are displayed to the user. When the user clicks on an item in the user interface, they'll be brought to the "details" view for that item in a second view. I'm currently using an identity property in my class to reference unique items in the list, but I'm wondering if there is a better way.
I'm using System.Windows.Navigationto set up UriMappings like so:
<nav:UriMapping Uri="/Items/{itemId}"
MappedUri="/Views/Items.xaml?itemId={itemID}" />
I then use the NavigationService to navigate like so:
NavigationService.Navigate(new Uri("/Items/" + item.id.ToString(),
UriKind.Relative));
All of this works great, but I don't know that I like needing to include an identity column for my Item class just so that can have a unique string identifer to pass in to the MappedUri query string. My user interface control that shows the list gives me the underlying Item object in the SelectionChanged event, so I'm wondering if there's a way to "match" that Item object with a unique string value to its corresponding reference in the list. If I could provide a simple object reference to the navigation service, this would be easy, but I'm not sure how to do it with a string value. Is this what GetHashCode() is for, and if so, how do I use it correctly?
No, GetHashCode is not intended to uniquely identify objects.
The default implementation of the GetHashCode method does not guarantee unique return values for different objects. Furthermore, the .NET Framework does not guarantee the default implementation of the GetHashCode method, and the value it returns will be the same between different versions of the .NET Framework. Consequently, the default implementation of this method must not be used as a unique object identifier for hashing purposes.
It is designed for storing objects in buckets (for example in a dictionary) so that they can be quickly retrieved. The hash codes for two equal objects must be equal, but the hash codes for two different objects do not have to be different.
I think adding an Id field is a fine solution. If you don't like adding a new field just to give an object an identity you can try to find some combination of the existing fields that is guarateed to uniquely identify your object and concetenate their string representations with a suitable separator. Be careful that the separator cannot occur in any of the fields.
There's a GUID which you can use that maps to each string in the list, by calling a NewGuid method of the Guid class, one can generate a Guid and use that as a string type via ToStringmethod and use it for hashing strings.
You definitely want to provide the item's ID. Bear in mind that your application may be tombstoned, so the object may not even be in memory any more when you try to navigate to that page. You need to be able to restore the application to the page with no information other than the URL and whatever's been serialized to temporary or permanent storage (typically in OnNavigateFrom).
Using GetHashCode() for this not only doesn't guarantee uniqueness, but it's highly unlikely to work in terms of the newly restored application, too. Life gets a lot harder when you're trying to display the details of something where you haven't really got an ID... but it doesn't sound like you're really in that position.
As it looks like you've got an ID, use it as an ID. Why would you want to use anything else? If you don't have an ID but can easily add one, do so. You want a way of uniquely identifying an object... that's exactly what an ID is for.

Categories