简体   繁体   中英

ObservableCollection<T>(List<T>)

In msdn you can read: "Initializes a new instance of the ObservableCollection class that contains elements copied from the specified list."

But I don't understand this behaviour:

I have got a class Person.

public class Person
{
    public string Firstname { get; set; }
    public string Lastname { get; set; }

    public override string ToString()
    {
        return Firstname + " " + Lastname;
    }
}

Now i create a List of persons. Then i create an ObservableCollection that should contain copied elements from the list.

Then I change one person in the list, and one person in the ObservableCollection. Both changes are reflected in both collections. Why?

Finally I add one person to the list and one to the OC. The added items are only reflectet in the related collection

public partial class MainWindow : Window
{
    private List<Person> PersonList{ get; set; }
    private ObservableCollection<Person> PersonObservableCollection { get; set; }

    public MainWindow()
    {
        InitializeComponent();

        FillCollections();

        listbox1.ItemsSource = PersonList;
        listbox2.ItemsSource = PersonObservableCollection;            
    }


    private void FillCollections()
    {
        PersonList = LoadDataPerson();
        PersonObservableCollection = new ObservableCollection<Person>(PersonList);

        // Adding a person to the List.
        PersonList.Add(new Person() { Firstname = "added to List",});

        // Adding a person to the ObservableCollection.
        PersonObservableCollection.Add(new Person() { Firstname = "added to Observable Collection" });


        // Changing then name of the first person in the List
        Person p1 = PersonList[0];
        p1.Lastname = "changed in List";

        // Changing the name of the second person in the ObservableList
        Person p2 = PersonObservableCollection[1];
        p2.Lastname = "changed in ObservableCollection";
    }


    private List<Person> LoadDataPerson()
    {
        List<Person> personen = new List<Person>();
        personen.Add(new Person() { Firstname = "John"});
        personen.Add(new Person() { Firstname = "Will"});
        personen.Add(new Person() { Firstname = "Sam" });
        return personen;
    }
}

xaml:

<Window x:Class="ObservableCollection.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:ObservableCollection"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>

    <ListBox Grid.Column="0" x:Name="listbox1"/>
    <ListBox Grid.Column="1" x:Name="listbox2"/>

</Grid>

The output looks like this: Output

The documentation could be clearer there. For a value type, like int , it's obvious what "copied" means in:

Initializes a new instance of the ObservableCollection<T> class that contains elements copied from the specified list.

In your case however, you have a reference type. It's this reference that's being copied between the two lists, so both lists are pointing at the same underlying object, meaning any updates are seen in both.

In msdn you can read: "Initializes a new instance of the ObservableCollection class that contains elements copied from the specified list."

That is true, but what is being copies are the elements, not their content - meaning the reference for the elements.

So now you have two collection that have the same elements.
When you will ask an element from the list or the observableCollection you will get a reference to the same instance of person. So changing it would, obviously reflect in both.

When you add a new item, you add it to a specific collection and it won't be copied to the other one.

So if you look at the source code for this the underlying list is being manipulated.

    protected virtual void InsertItem(int index, T item) {
        items.Insert(index, item);
    }

that is just one example, with items being a private field:

public class Collection<T>: IList<T>, IList, IReadOnlyList<T>
{
    IList<T> items;
    [NonSerialized]
    private Object _syncRoot;

    public Collection() {
        items = new List<T>();
    }

    public Collection(IList<T> list) {
        if (list == null) {
            ThrowHelper.ThrowArgumentNullException(ExceptionArgument.list);
        }
        items = list;
    }

As you can clearly see when you modify both collections (the observable one and your original one), the changes will be reflected in both.

All the ObservableCollection is doing over the top is proxying the INotifyPropertyChanged .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM