迭代对象类型时如何添加ICollection属性?

时间:2012-05-15 15:50:09

标签: c# ienumerable icollection

我需要添加一个类的ICollection<string>属性,其中我有一个IEnumerable。这是一个完整的程序,说明了问题:

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属性有什么特别之处?

5 个答案:

答案 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 :(得分:2)

问题是oppDocResult是使用延迟执行的LINQ查询的结果。

换句话说,每次迭代它时,查询都会执行,并且会创建 new OppDocumentServiceResult个对象。如果您在OppDocumentServiceResult构造函数中放置诊断程序,您将看到它。

因此,您在最后迭代的OppDocumentServiceResult个对象与您添加原因的对象不同。

现在,如果您添加ToList()电话,那么将查询具体化到“普通”集合(List<OppDocumentServiceResult>)。每次迭代该列表时,它都会返回对相同对象的引用 - 所以如果你第一次迭代它们时添加原因,那么当你再次遍历它们时打印出原因,你会得到结果你正在寻找。

有关更多详细信息,请参阅this blog post(在“LINQ延期执行”的许多搜索结果中)。

答案 2 :(得分: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>

答案 3 :(得分:1)

修正了这样,转换为List而不是保留IEnumerable:

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

我只能猜测(这真的是在黑暗中拍摄!)这背后的原因是在IEnumerable中元素就像真实元素的“代理”一样?基本上Linq查询定义的Enumerable就像一个获取所有数据的“承诺”,所以每次迭代你都会得到原始项目?这并不能解释为什么正常的财产仍然坚持......

所以,修复就在那里,但我担心的解释不是......至少不是来自我:(

答案 4 :(得分:0)

只需在课堂内更改代码

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