2012-05-15 3 views
0

Мне нужно добавить к собственности ICollection<string> класса, в котором у меня есть IEnumerable. Вот полная программа, которая иллюстрирует проблему:Как добавить свойство ICollection при итерации типа объекта?

using System; 
using System.Collections.Generic; 
using System.Linq; 

namespace CollectionAddingTest 
{ 
    public class OppDocumentServiceResult 
    { 
     public OppDocumentServiceResult() 
     { 
      this.Reasons = new List<string>(); 
     } 

     public Document Document { get; set; } 

     public bool CanBeCompleted 
     { 
      get 
      { 
       return !Reasons.Any(); 
      } 
     } 

     public ICollection<string> Reasons { get; private set; } 
    } 

    public class Document 
    { 
     public virtual string Name { get; set; } 
    } 

    public class Program 
    { 
     private static void Main(string[] args) 
     { 
      var docnames = new List<string>(new[] {"test", "test2"}); 

      var oppDocResult = docnames 
       .Select(docName 
         => new OppDocumentServiceResult 
           { 
            Document = new Document { Name = docName } 
           }); 

      foreach (var result in oppDocResult) 
      { 
       result.Document.Name = "works?"; 
       result.Reasons.Add("does not stick"); 
       result.Reasons.Add("still does not stick"); 
      } 

      foreach (var result in oppDocResult) 
      { 
       // doesn't write "works?" 
       Console.WriteLine(result.Document.Name); 

       foreach (var reason in result.Reasons) 
       { 
        // doesn't even get here 
        Console.WriteLine("\t{0}", reason); 
       } 
      } 
     } 
    } 
} 

Я бы ожидать, что каждый OppDocumentServiceResult бы его ссылки Document.Name набор свойств для работ?, и каждый OppDocumentServiceResult должен иметь две причины. Однако ни то, ни другое не происходит.

Что такое свойство «Причины», которое я не могу добавить к нему?

+1

Он должен работать абсолютно нормально. Пожалуйста, предоставьте краткую, но * полную * программу, демонстрирующую проблему. –

+0

@JonSkeet изменил приведенное выше, чтобы быть немного более полным. Ясно, как грязь? –

+0

Не совсем - это еще не краткая, но полная программа, которую мы можем использовать, чтобы точно выработать то, что происходит ... –

ответ

1

Фиксированный как это, преобразование в список вместо того, чтобы в IEnumerable:

var oppDocResult = docnames 
     .Where(docName => !String.IsNullOrEmpty(docName)) 
     .Select(docName 
      => new OppDocumentServiceResult 
      { 
       Document = docName 
      }).ToList(); 

Я могу только догадываться (это выстрел в темноте действительно!), что причина в том, что в IEnumerable элементы похожи на «прокси» реальных элементов? в основном Enumerable, определяемый запросом Linq, подобен «обещанию» для получения всех данных, поэтому каждый раз, когда вы повторяете, вы возвращаете исходные элементы? Это не объясняет, почему нормальное свойство до сих пор торчит ...

Таким образом, исправление там, но объяснение, которое я боюсь, не ... не от меня, по крайней мере :(

+0

Спасибо за «выстрел в темноте». Я опубликовал полную программу, которая иллюстрирует проблему. Может быть, мы доберемся туда. :) –

+0

Причина в том, что каждый раз, когда вы 'foreach' над' oppDocResults', вы получаете последовательность с совершенно новыми 'OppDocuemntServiceResults', а не получаете те же' OppDocuemntServiceResults', что использовалась предыдущая итерация. По 'ToList'у результатов вы гарантируете, что повторение по списку повторно всегда возвращает одни и те же объекты. Поскольку теперь вы получаете одни и те же объекты, вы можете увидеть изменения, сделанные в предыдущих итерациях. – Servy

+0

Я добавил объяснение в свой ответ. –

1

ForEach() определяется только против List<T> вы не быть в состоянии использовать его для ICollection<T>.

Вы должны варианты:

((List<string>) Reasons).ForEach(...) 

Или

Reasons.ToList().ForEach(...) 

Тем не менее, мой предпочтительный подход

Я бы определил это расширение, которое может помочь автоматизировать это для вас без потерь ресурсов:

public static class ICollectionExtensions 
{ 
    public static void ForEach(this ICollection<T> collection, Action<T> action) 
    { 
     var list = collection as List<T>; 
     if(list==null) 
      collection.ToList().ForEach(action); 
     else 
      list.ForEach(action); 
    } 
} 

Теперь я могу использовать ForEach() против ICollection<T>.

+0

Согласен. Я указывал, что с помощью ToList() Foreach в IEnumerable не работал при добавлении к основному свойству 'OppDocumentServiceResult.Reasons'. У меня была бы ошибка времени компиляции. –

+0

@ RussClark Да, я получил ваш вопрос, прежде чем вы обновили свой пост. – Aliostad

+0

, но то, что вы предлагаете, все еще находится на уровне собственности. Я заинтересован в изменении коллекции «Причины» для каждого «OppDocumentServiceResult» в IEnumerable. –

0

Просто изменить код внутри класса

public List<string> Reasons { get; private set; } 
+0

Я уже пробовал это, но на всякий случай попробовал это снова. Не имеет значения. –

2

Вопрос ваш первоначальный Select вы инстанцирование новых OppDocumentServiceResult объектов Добавить ToList и вы должны быть хорошо идти:.

var oppDocResult = docnames 
    .Select(docName 
      => new OppDocumentServiceResult 
        { 
         Document = new Document { Name = docName } 
        }).ToList(); 

Как Servy указал я добавил немного более подробно на мой ответ, но, к счастью the comment он оставил на Tallmaris' answer принимает забота об этом. В своем ответе Джон Ск eet далее расширяется по причине, но то, что она сводится к «заключается в том, что oppDocResult является результатом запроса LINQ с использованием отложенного выполнения».

+1

Это должно решить проблему, но, вероятно, стоит объяснить * почему * она решает проблему. – Servy

+0

@Servy добавил немного объяснений, но был связан с вашим комментарием и ответом Джона Скита для более подробного объяснения. – ahsteele

2

Проблема заключается в том, что oppDocResult является результатом запроса LINQ с использованием отложенного выполнения ,

Другими словами, каждый раз, когда вы перебираете его, выполняется запрос и создаются OppDocumentServiceResult. Если вы поместите диагностику в конструктор OppDocumentServiceResult, вы увидите это.

Так что объекты OppDocumentServiceResult, которые вы повторяете в конце, отличаются от тех, к которым вы добавили причины.

Теперь, если вы добавите ToList() вызов, то, что материализуется запрос в «простой» коллекции (а List<OppDocumentServiceResult>). Каждый раз, когда вы перебираете этот список, он будет возвращать ссылки на одни и те же объекты, поэтому, если вы добавите причины при первом переходе по ним, вы распечатываете причины, когда вы повторите их снова, вы получите результаты, которые вы «Ищем.

См. this blog post (среди многих результатов поиска для «отложенного выполнения LINQ») для получения более подробной информации.

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