简体   繁体   English

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

[英]How do I add to an ICollection property when iterating object type?

I have need to add to an ICollection<string> property of a class of which I have an IEnumerable of. 我必须添加一个具有IEnumerable的类的ICollection<string>属性。 Here is a complete program which illustrates the problem: 这是说明问题的完整程序:

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);
                }
            }
        }
    }
}

I would expect that each OppDocumentServiceResult would have its referenced Document.Name property set to works? 我希望每个OppDocumentServiceResult都将其引用的Document.Name属性设置为有效吗? and each OppDocumentServiceResult should have two reasons added to it. 并且每个OppDocumentServiceResult应该添加两个原因。 However, neither is happening. 但是,都没有发生。

What is special about the Reasons property that I cannot add things to it? 无法添加内容的原因属性有什么特别之处?

The issue is your initial Select you are instantiating new OppDocumentServiceResult objects. 问题是您最初Select要实例化新的OppDocumentServiceResult对象。 Add a ToList and you should be good to go: 添加一个ToList ,您应该会很好:

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

As Servy pointed out I should have added a bit more detail to my answer, but thankfully the comment he left on Tallmaris' answer takes care of that. 正如Servy指出的那样,我应该在回答中添加更多细节,但值得庆幸的是,他在Tallmaris回答中留下的评论可以 解决这一问题。 In his answer Jon Skeet further expands on the reason, but what it boils down to "is that oppDocResult is the result of a LINQ query, using deferred execution." 乔恩·斯凯特(Jon Skeet)在回答中进一步阐述了原因,但归结为“ oppDocResult是使用延迟执行的LINQ查询的结果”。

The problem is that oppDocResult is the result of a LINQ query, using deferred execution. 问题在于oppDocResult是使用延迟执行的LINQ查询的结果。

In other words, every time you iterate over it, the query executes and new OppDocumentServiceResult objects are created. 换句话说,每次您对其进行迭代时,都会执行查询并创建新的 OppDocumentServiceResult对象。 If you put diagnostics in the OppDocumentServiceResult constructor, you'll see that. 如果将诊断放在OppDocumentServiceResult构造函数中,您将看到。

So the OppDocumentServiceResult objects you're iterating at the end are different ones to the ones you've added the reasons to. 因此,您最后要迭代的OppDocumentServiceResult对象与您添加了原因的对象不同。

Now if you add a ToList() call, then that materializes the query into a "plain" collection (a List<OppDocumentServiceResult> ). 现在,如果你添加一个ToList()调用,那么物化查询到一个“纯”集合( List<OppDocumentServiceResult> Each time you iterate over that list, it will return references to the same objects - so if you add reasons the first time you iterate over them, then you print out the reasons when you iterate over them again, you'll get the results you're looking for. 每次在该列表上进行迭代时,它都会返回对相同对象的引用-因此,如果您在第一次对其进行迭代时添加原因,那么当您再次对其进行迭代时,您将打印出原因,您将获得结果正在寻找。

See this blog post (among many search results for "LINQ deferred execution") for more details. 有关更多详细信息,请参见此博客文章 (在“ LINQ延迟执行”的许多搜索结果中)。

ForEach() is defined against only List<T> you will not be able to use it to for an ICollection<T> . 仅针对List<T>定义ForEach()您将无法将其用于ICollection<T>

You have to options: 您必须选择:

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

Or 要么

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

Yet, my preferred approach 但是, 我偏爱的方法

I would define this extension which can help automating this for you without wasting resources: 我将定义此扩展名,它可以帮助您自动执行此扩展,而不会浪费资源:

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);
    }
}

Now I can use ForEach() against ICollection<T> . 现在,我可以对ICollection<T>使用ForEach()

Fixed like this, converting to List instead of keeping the IEnumerable: 固定如下,将其转换为List而不是保持IEnumerable:

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

I can only guess (this is a shot in the dark really!) that the reason behind this is that in an IEnumerable the elements are like "proxies" of the real elements? 我只能猜测(这实际上是在黑暗中拍摄!)背后的原因是,在IEnumerable中,元素就像是真实元素的“代理”? basically the Enumerable defined by the Linq query is like a "promise" to get all the data, so everytime you iterate you get back the original items? 基本上,Linq查询定义的Enumerable就像获取所有数据的“承诺”一样,因此每次迭代时,您都会获得原始项目? That does not explain why a normal property still sticks... 这并不能解释为什么普通财产仍然存在……

So, the fix is there but the explanation I am afraid is not... not from me at least :( 所以,这里有解决方法,但我担心的解释不是……至少不是我的:(

只需在类中更改代码

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在类构造器中将icollection设置为列表时,如何通过索引从icollection中获取项目? - How do I get an item from an icollection via index when in the class contrusctor I set the icollection as a list? 如何在ICollection类型的属性上使用反射? - How to use reflection on a property of type ICollection? 如何使用反射迭代ICollection类型属性 - How to iterate through an ICollection type property with reflection 如何按ICollection的属性排序? - How do you sort by a property of an ICollection? 如何使用ICollection属性进行布尔检查 - How to use ICollection property to do a bool check 为什么我们需要创建Icollection类型属性的新实例 - why do we need to create new instence of Icollection type property 如何从对包含类型的引用中获取ICollection &lt;&gt;属性的名称? - How can I get the name of an ICollection<> property from a reference to the contained type? 如何添加到ICollection列表并将其保存到数据库而又不会出现对象处置异常? (实体框架) - How do you add to an ICollection list and save it to a database without gettingan object disposed exception? (Entity Framework) 获取泛型类型的ICollection属性 - Get ICollection property of generic type ICollection类型的访问导航属性 - Access Navigation Property of type ICollection
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM