2014-10-21 3 views
1

У меня есть коллекция объектов и вы хотите изменить свойство объекта в этой коллекции. Если у меня есть один объект, мой метод ChangeStuff работает нормально, и объект возвращается при возврате из метода. (Первые 4 строки в Main)Изменение свойства объекта в коллекции

Однако при повторении через коллекцию, я думаю, что я что-то упускаю, поскольку мои измененные значения, похоже, застревают в области цикла foreach.

Мне не нужно было передавать объекты by ref (или используя параметр out), так как я не возвращаю новое значение.

Извините за большой кусок кода, но он настолько упрощен, насколько я могу это сделать и все еще демонстрирую свою проблему.

class foobar 
    { 
     public string string1; 
     public string string2; 
    } 

    static void Main(string[] args) 
    { 
     /***** THIS WORKS!! *****/ 
     foobar singleFb = new foobar { string1 = "foo2", string2 = "bar2" }; 
     ChangeStuff(singleFb); 
     Console.WriteLine(singleFb.string1 + ", " + singleFb.string2); 

     Console.ReadLine(); //pause to read output 

     /***** THIS DOESN'T WORK!! *****/ 
     List<foobar> myList = new List<foobar> { new foobar {string1 = "foo1", string2 = "bar1"}, new foobar {string1 = "foo2", string2 = "bar2"}, new foobar {string1 = "something else", string2 = "something else again"} }; 
     IEnumerable<foobar> fbs = myList.Where(x => x.string1.StartsWith("foo")); 

     ChangeStuff(fbs); 

     foreach (foobar fb in fbs) 
     { 
      Console.WriteLine(fb.string1 + ", " + fb.string2); 
     } 
     Console.ReadLine(); //pause to read output 
    } 

    static void ChangeStuff(IEnumerable<foobar> fbs) 
    { 
     foreach (foobar fb in fbs) 
     { 
      ChangeStuff(fb); 
     } 
    } 

    static void ChangeStuff(foobar fb) 
    { 
     if (fb.string1.Contains("2")) 
      fb.string1 = "changed!"; 
    } 
} 

Что мне нужно изменить, чтобы изменить объект в коллекции?


Edit: Кроме того, только заметил, что моя коллекция на самом деле полностью отсутствует "foo2", когда он возвращается ... Weird. Я фактически использую IQueryable в своем приложении и не испытывал этой проблемы. то есть. У меня есть все объекты, они просто неправильно изменены. Не уверен, что здесь происходит ...


Edit 2: Спасибо за ваши ответы, это имеет смысл сейчас. Если бы я изменить свой ChangeStuff метод следующим образом, она работает, как я бы ожидать:

static void ChangeStuff(foobar fb) 
    { 
     if (fb.string2.Contains("2")) 
      fb.string2 = "changed!"; 
    } 
+0

Является ли «foobar» структурой? Пожалуйста, напишите краткий, но полный пример кода, который надежно демонстрирует проблему. –

+0

@PeterDuniho, извините, я пропустил копирование класса. См. Править. –

ответ

7

Результат, который вы видите, связан с ленивой нагрузкой IEnumerable<T>. Во второй раз, когда вы перечислите «fbs», свойство «string1» изменилось, так что оно больше не соответствует предикату Where.

Если перечислить IEnumerable сразу и сохранить результат в конкретном списке (например, с помощью ToList() или ToArray(), то второе перечисление покажет результат вы ожидаете:

IEnumerable<foobar> fbs = myList.Where(x => x.string1.StartsWith("foo")).ToList(); 

Вот что происходит в исходной версии кода:

List<foobar> myList = new List<foobar> { new foobar { string1 = "foo1", string2 = "bar1" }, new foobar { string1 = "foo2", string2 = "bar2" }, new foobar { string1 = "something else", string2 = "something else again" } }; 
IEnumerable<foobar> fbs = myList.Where(x => x.string1.StartsWith("foo")); 

// here the enumeration yields objects #1 and #2 
// object #2 has its "string1" property modified to "changed!" 
ChangeStuff(fbs); 

// here the enumeration is re-evaluated, and now object #2 no longer matches the predicate 
// only object #1 ("foo1") is output 
foreach (foobar s in fbs) 
{ 
    Console.WriteLine(s.string1); 
} 
+0

Спасибо! Делает совершенный смысл (см. Edit2). Я предполагаю, что это работает аналогично с IQueryable - при печати свойств он будет перезагружаться из db, и мои предыдущие изменения будут потеряны? –

+0

Я не уверен, что ваши изменения будут уничтожены (то есть я думаю, что, возможно, контекст данных все равно сохранит ваши изменения, и они будут отправлены в БД при вызове «SubmitChanges»?). Но да, 'IQueryable' похож на' IEnumerable', так как он будет поступать в источник каждый раз, если только вы не поймете его в конкретном списке. – McGarnagle

5

Самое главное, что нужно помнить при работе с LINQ является то, что методы не возвращают результаты запроса, они возвращаются объект, который представляет сам запрос. Это когда последовательность перечисляется, что запрос выполняется и возвращаемые элементы.

fbs является запрос получить все элементы, где первая строка начинается с foo. Это не коллекция первых двух элементов, хотя это те элементы, которые будут возвращены, если вы выполните этот запрос, когда вы его впервые определили.

При первом выполнении запроса и повторении результатов вы возвращаете два элемента и пытаетесь изменить их оба, только один фактически изменяется на ChangeStuff. Затем вы выполните запрос , чтобы распечатать результаты, но теперь измененный элемент не соответствует критериям запроса и поэтому не возвращается. Возвращается только первый элемент, который не был изменен. Если вы выполните итерирование списка, а не ваш запрос, вы увидите свой измененный элемент.

+0

Ahhh ... это имеет смысл. Похоже, мой лучший вариант - преобразовать .ToList() перед редактированием коллекции? –

+0

@ sǝɯɐs Это сработает, но в этом случае печать результатов должна, вероятно, просто распечатать весь список. Это было бы правильным испытанием в моих глазах. Несмотря на это, дело в том, что вы просто неправильно наблюдаете изменения, которые действительно происходят. – Servy

+0

Спасибо за отличный ответ, и мне нравится курсив, но мне нужно будет дать очки @McGarnagle, так как его пример кода сделал это немного понятнее для меня. –

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