繁体   English   中英

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

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM