2011-01-10 4 views
0

У меня возникли проблемы с пониманием того, что происходит, когда ToArray() вызывается в IEnumerable. Я всегда предполагал, что копируются только ссылки.Что происходит, когда ToArray() вызывается в IEnumerable?

Я хотел бы ожидать выход здесь, чтобы быть: истинное истинное

Но вместо этого я получаю правда ложь

Что здесь происходит?

class One { 
    public bool Foo { get; set; } 
} 

class Two 
{ 
    public bool Foo { get; set; } 
} 

void Main() 
{ 
    var collection1 = new[] { new One(), new One() }; 

    IEnumerable<Two> stuff = Convert(collection1); 

    var firstOne = stuff.First(); 

    firstOne.Foo = true; 

    Console.WriteLine (firstOne.Foo); 

    var array = stuff.ToArray(); 

    Console.WriteLine (array[0].Foo); 
} 

IEnumerable<Two> Convert(IEnumerable<One> col1) { 

    return 
     from c in col1 
     select new Two() { 
      Foo = c.Foo 
     }; 
} 

ответ

4

Проблема заключается в следующем. Когда линия

var firstOne = stuff.First(); 

выполнена, коллекция collection1 итеративна и новый экземпляр Two возвращается. Когда линия

var array = stuff.ToArray(); 

выполнена, коллекция collection повторяется снова и новые экземпляры Two будут возвращены, в том числе нового экземпляра для первого элемента коллекции. В частности, это другой пример, чем firstOne. Следовательно, будет Object.ReferenceEquals(firstOne, array[0])false (обратите внимание, что как написано, Object.ReferenceEquals (stuff.First(), stuff.First()) будет false). Это причина проблемы, которую вы видите.

Чтобы обойти эту проблему, вы должны сказать

IEnumerable<Two> stuff = Convert(collection1).ToList(); 

или

IEnumerable<Two> Convert(IEnumerable<One> col1) { 
    return col1.Select(x => new Two { Foo = x.Foo}).ToList(); 
} 

, так что в любом случае, collection1 повторяется ровно один раз, а затем Object.ReferenceEquals(firstOne, array[0]) будет true.

+0

Чтобы быть ясным, проблема связана с комбинацией повторной итерации и создания новых объектов при каждом перечислении. Если перечисление вернуло исходные объекты - без создания новых экземпляров, то повторная итерация все равно будет извлекать исходный обновленный объект. –

4

.ToArray() is red herring here. В вашей Convert функции, вы создаете полностью несвязанный экземпляр Two и установив его свойство в логическое значение (который не ссылочный тип), вы принимаете от экземпляра One. Любые изменения во вновь созданном экземпляре Two будут не влиять на экземпляр One. Они никоим образом не связаны.

Если Convert сделал это:

IEnumerable<One> Convert(IEnumerable<One> input) { 
    return from i in input 
      select i; 
} 

вы получите результат, который вы ожидали.

+0

Он фактически работает только с преобразованным списком, в этом случае класс One не оказывает на него никакого влияния. – Rob

+0

@Rob: 'One' является ссылочным типом. В моей версии 'Convert' нет' new One {..} '. Он просто возвращает ссылку на тот же экземпляр. –

+1

Я понимаю, что, однако, проблема не в функции преобразования. Он устанавливает свойство Foo в возвращенной коллекции, а не в исходной коллекции. Если он удалил один класс и запустил тот же код без конвертации (просто создайте коллекцию из двух, эта же проблема возникает). – Rob

Смежные вопросы