![](/img/trans.png)
[英]How do I get an item from an icollection via index when in the class contrusctor I set the icollection as a list?
[英]How do I add to an ICollection property when iterating object type?
我必须添加一个具有IEnumerable
的类的ICollection<string>
属性。 这是说明问题的完整程序:
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应该添加两个原因。 但是,都没有发生。
无法添加内容的原因属性有什么特别之处?
问题是您最初Select
要实例化新的OppDocumentServiceResult
对象。 添加一个ToList
,您应该会很好:
var oppDocResult = docnames
.Select(docName
=> new OppDocumentServiceResult
{
Document = new Document { Name = docName }
}).ToList();
正如Servy指出的那样,我应该在回答中添加更多细节,但值得庆幸的是,他在Tallmaris回答中留下的评论可以 解决这一问题。 乔恩·斯凯特(Jon Skeet)在回答中进一步阐述了原因,但归结为“ oppDocResult是使用延迟执行的LINQ查询的结果”。
问题在于oppDocResult
是使用延迟执行的LINQ查询的结果。
换句话说,每次您对其进行迭代时,都会执行查询并创建新的 OppDocumentServiceResult
对象。 如果将诊断放在OppDocumentServiceResult
构造函数中,您将看到。
因此,您最后要迭代的OppDocumentServiceResult
对象与您添加了原因的对象不同。
现在,如果你添加一个ToList()
调用,那么物化查询到一个“纯”集合( List<OppDocumentServiceResult>
每次在该列表上进行迭代时,它都会返回对相同对象的引用-因此,如果您在第一次对其进行迭代时添加原因,那么当您再次对其进行迭代时,您将打印出原因,您将获得结果正在寻找。
有关更多详细信息,请参见此博客文章 (在“ LINQ延迟执行”的许多搜索结果中)。
仅针对List<T>
定义ForEach()
您将无法将其用于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);
}
}
现在,我可以对ICollection<T>
使用ForEach()
。
固定如下,将其转换为List而不是保持IEnumerable:
var oppDocResult = docnames
.Where(docName => !String.IsNullOrEmpty(docName))
.Select(docName
=> new OppDocumentServiceResult
{
Document = docName
}).ToList();
我只能猜测(这实际上是在黑暗中拍摄!)背后的原因是,在IEnumerable中,元素就像是真实元素的“代理”? 基本上,Linq查询定义的Enumerable就像获取所有数据的“承诺”一样,因此每次迭代时,您都会获得原始项目? 这并不能解释为什么普通财产仍然存在……
所以,这里有解决方法,但我担心的解释不是……至少不是我的:(
只需在类中更改代码
public List<string> Reasons { get; private set; }
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.