2012-05-15 55 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應該有兩個原因添加到它。但是,兩者都沒有發生。

對於Reasons屬性我有什麼特別之處?

+1

它應該工作絕對好。請提供一個簡短的*完整*程序來證明問題。 –

+0

@JonSkeet修改了上面的要更加完整。清除泥漿? –

+0

不是真的 - 它仍然不是一個簡短但完整的程序,我們可以使用它來確切地確定發生了什麼...... –

回答

1

固定這樣,轉換成列表,而不是保持了IEnumerable:

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

我只能猜測(這是一個在黑暗中拍攝真的!)這背後的原因是,在IEnumerable中,元素就像真實元素的「代理」?基本上由Linq查詢定義的Enumerable就像是一個獲取所有數據的「承諾」,所以每次迭代時都會返回原始項目?這並不能解釋爲什麼一個正常的財產仍然堅持......

所以,解決辦法是有,但恐怕解釋是不是...不是從我至少:(

+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()。添加到底層的「OppDocumentServiceResult.Reasons」屬性時,Foreach在IEnumerable 上不起作用。我會有一個編譯時錯誤。 –

+0

@RussClark是的,在更新你的帖子之前,我明白了你的意思。 – Aliostad

+0

但是你建議的還是在物業層面。我有興趣修改IEnumerable中每個「OppDocumentServiceResult」的Reasons集合。 –

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需要在他的回答Jon Skeet進一步擴大了原因,但它歸結爲「是oppDocResult是LINQ查詢的結果,使用延遲執行」。

+1

這應該解決問題,但它可能值得解釋*爲什麼它解決了這個問題。 – Servy

+0

@Servy增加了一些解釋,但與您的評論和Jon Skeet的回答有關的更詳細的解釋。 – ahsteele

2

問題是oppDocResult是使用延遲執行的LINQ查詢的結果。

換句話說,每次迭代它時,查詢都會執行並且新的OppDocumentServiceResult對象被創建。如果您將診斷程序置於OppDocumentServiceResult構造函數中,您會看到。

所以最後迭代的OppDocumentServiceResult對象與您添加原因的對象不同。

現在,如果你添加一個ToList()調用,那麼物化查詢到一個「純」集合(List<OppDocumentServiceResult>)。每次迭代該列表時,它都會返回對相同對象的引用 - 所以如果在第一次迭代它們時添加原因,那麼當您再次遍歷它們時打印出原因,就會得到結果正在尋找。

請參閱this blog post(在「LINQ延遲執行」的許多搜索結果中)以獲取更多詳細信息。