IEnumerable可能的多次枚举。怎么解决?我需要解决吗?

问题描述 投票:3回答:8

我有以下代码,在标题中给出警告。我很确定我以前做过这样的事情,但它没有发出任何警告。我想问一下这些帖子的两件事。 1)这会导致什么问题? 2)是否需要修复?

我问的原因是这个代码工作得很好,因为我预计这个警告不会引起问题。我不能忍受在我的代码中有警告等,所以想要解决这个问题,但我也想知道为什么会发生这种警告以及它是否有害。

码:

 public class AttributeType
 {
      private string m_attributeNameField;

      public string AttributeName
      {
          get { return m_attributeNameField; }
          set { m_attributeNameField = value; }
      }
 }

 private StandardResponseType ValidateAttributes(string featureType, IEnumerable<AttributeType> attributeList, string userCategory)
 {
       StandardResponseType standardResponse = 
       new StandardResponseType(DateTime.Now.ToString(CultureInfo.InvariantCulture), "RWOL_UTILS.Get_Item_Attributes", "", "OK");

        if (attributeList.Any())
        {
            foreach (AttributeType attribute in attributeList)
            {
                if (attribute.AttributeName == null) continue;
                {
                    //do stuff
                }
            }
        }
        else
        {
            standardResponse.Message = "Error: No attributes passed in the list. ValidateAttributes().";
            standardResponse.ResponseCode = "FAIL";

            return standardResponse;
        }
}

编辑:方法中有更多的代码,但它与此问题无关。

更新:我必须添加以下代码才能使其工作。为什么添加它更有效?如果我必须计算并阅读新列表,那么在原始项目上执行此操作和执行此操作之间的区别是什么?该列表只传递过一次。如果列表是在方法中填充但是不是,我可以理解这个问题。它刚刚被填充了。

List<AttributeType> newlist = attributeList.ToList();

if (newlist.Count() != 0)
{
    foreach (AttributeType attribute in newlist)
............
c# ienumerable
8个回答
3
投票

可能的问题取决于你的IEnumerable来自哪里。某些数据源可能只允许单个枚举,或者它们可能很昂贵(可能是某些数据库查询),这些数据源已经由attributeList.Any()启动。

您可以删除Any()检查,因为如果IEnumerable中没有元素,则无论如何都不会运行循环(假设您的示例显示完整的图片,并且没有其他逻辑依赖于检查)。

编辑:根据您编辑的问题,您无法删除支票。但是,您可以使用attributeList.ToArray()将您的IEnumerable转换为您随后使用的数组并消除警告。


6
投票

摆脱if;这毫无用处。

警告来自Resharper,它警告你,如果attributeList枚举成本很高,你的代码会很慢。 (因为它列举了一次Any()和第二次foreach


1
投票

原因是调用attributeList.Any()attributeList开始,一旦发现它进入你的for循环。然后你在列表上做一个foreach,它再次遍历整个列表。

你实际上不需要这里的.Any(),因为对一个空的可枚举做一个foreach不会导致任何问题,它只是不会返回任何东西。

你可能遇到的问题是,如果你从数据库中提取数据并且在foreach内部再次调用枚举,因为它是延迟执行的,你可能会得到不同的结果,这是你第二次调用时没想到的。


1
投票

我猜attributeList是某种类型的IEnumerable<>。与IEnumerables不同,List对象不一定是内存中对象的列表,并且可能绑定到复杂的逻辑,每次迭代它时都会查询DB。使用C#的yield return命令也可以返回一个IEnumerable,它具有与每次迭代绑定的逻辑。

由于此行为,警告会告诉您可能会多次迭代您的属性,这可能是一项可能很昂贵的操作。一次在Any()期间,一次在foreach。确实,在这种情况下,Any()是多余的,但一般来说,您可以通过在ToList()上调用ToArray()IEnumerable来避免此警告,从而执行枚举一次并将结果存储在显式分配的列表/数组中。现在你可以一次又一次地重复它,没有性能影响。


0
投票

不,你不需要解决它。但是,如果你的if (attributeList.Any())没有else,你可以完全消除它,这将消除警告。实际上,您的代码示例可以替换为:

foreach (AttributeType attribute in attributeList.OfType<AttributeType>())
{
    // do stuff
}

0
投票

你不必检查.Any()这里是固定代码:

        bool empty = true;
        foreach (AttributeType attribute in attributeList) 
        { 
            empty = false;
            if (attribute.AttributeName == null) continue; 
            { 
                //do stuff 
            } 
        } 
        if (empty)
        {
          {  
              standardResponse.Message = "Error: No attributes passed in the list.       ValidateAttributes().";  
              standardResponse.ResponseCode = "FAIL";  

              return standardResponse;  
          }  
        }

检查.Any()会导致枚举,这就是为什么你得到一个警告=>第一个枚举“Any”来检查它是否为空,第二个枚举是“foreach”。

如果attributeList的类型允许,您可以检查Count或Length:

    if (attributeList.Count != 0)         
    {         
        foreach (AttributeType attribute in attributeList)         
        {         
            if (attribute.AttributeName == null) continue;         
            {         
                //do stuff         
            }         
        }         
    }         
    else         
    {         
        standardResponse.Message = "Error: No attributes passed in the list. ValidateAttributes().";         
        standardResponse.ResponseCode = "FAIL";         

        return standardResponse;         
    } 

0
投票

我能够允许你这样做,它更漂亮:

但无论如何,当你调用.Any()时,它只会迭代第一个项目,所以它真的不是那么糟糕。

这种方式更具可读性,虽然在背景中有点难看(仍然有效)。

        bool any;

        foreach (var i in Enumerable.Range(0, 100).Loop(out any))
        {
            // Do loop logic
        }

        if (!any)
        {
            // Handle empty IEnumerable
        }

怎么样?!:

public static class Ex
{
    public static IEnumerable<T> Loop<T>(this IEnumerable<T> source, out bool any)
    {
        var b = true;

        var enumerable = source.Loop(() => { b = false; });

        any = b;

        return enumerable;
    }

    private static IEnumerable<T> Loop<T>(this IEnumerable<T> source, Action anySetter)
    {
        var enumerator = source.GetEnumerator();

        enumerator.Reset();

        if (!enumerator.MoveNext())
        {
            anySetter();
            yield break;
        }

        do
        {
            yield return enumerator.Current;
        } while (enumerator.MoveNext());
    }
}

0
投票

因为我在查询数据集时搜索了这个确切的问题,所以我去添加.Any()并发现我可以添加.AsQueryable()到我选择的结尾,我的foreach使用它。 .AsQueryable将通用IEnumerable转换为通用IQueryable。

© www.soinside.com 2019 - 2024. All rights reserved.