【问题标题】:Convert a sequence of arrays T myArray[] to IEnumerable<T> in c#在 c# 中将数组序列 T myArray[] 转换为 IEnumerable<T>
【发布时间】:2011-07-06 01:01:53
【问题描述】:

在下面的代码中,我想在不创建新数据结构对象的情况下返回一个 IEnumerable。但是,我收到以下代码的编译器错误。我错过了什么?

Error       Cannot implicitly convert type 'System.Reflection.FieldInfo[]' to 'System.Reflection.FieldInfo' 

public static IEnumerable<FieldInfo> GetAllFields(Type objectType)
{
   while (objectType != null)
   {
      //GetFields(...) returns a FieldInfo []
      yield return objectType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
      objectType = objectType.BaseType;
   }
}

【问题讨论】:

    标签: c# linq enumerable


    【解决方案1】:

    正如大家所指出的,问题在于产生的元素的类型与预期的类型不匹配。

    为了避免每次都写一个 for/while/foreach,我喜欢使用一个扩展方法来为我处理迭代。

    public static class Sequence
    {
        public static IEnumerable<T> Create<T>(T seed, Func<T, bool> predicate, Func<T, T> next)
        {
            for (T t = seed; predicate(t); t = next(t))
                yield return t;
        }
    }
    

    这样,您可以编写更具可读性的查询返回字段

    public static IEnumerable<FieldInfo> GetAllFields(Type objectType)
    {
        BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
        return from type in Sequence.Create(objectType, t => t != null, t => t.BaseType)
            from field in type.GetFields(flags)
            select field;
    }
    

    【讨论】:

      【解决方案2】:

      稍微递归一下怎么样?

      public static IEnumerable<FieldInfo> GetAllFields(Type objectType)
      {      
            //GetFields(...) returns a FieldInfo []
            var fields = objectType.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
            if(objectType.BaseType==null) return fields;
            return fields.Concat(GetAllFields(object.BaseType));
       }
      

      【讨论】:

        【解决方案3】:

        你可以试试递归+Concat:

        public static IEnumerable<FieldInfo> GetAllFields(Type objectType)
        {
           if (objectType == null)
              return Enumerable<FieldInfo>.Empty;
           else 
              return objectType.GetFields(
                         BindingFlags.NonPublic | BindingFlags.Public |
                         BindingFlags.Instance | BindingFlags.DeclaredOnly)
                 .Concat(GetAllFields(objectType.BaseType));
        }
        

        【讨论】:

        • 我最喜欢这个解决方案,但我发现我真正的问题是尝试在整个数组上使用 yield 关键字。
        【解决方案4】:

        yield return 对单个元素进行操作;你可以简单地从GetFields返回返回值来实现你想要的。

        为了进一步解释,yield return 主要在您希望在将其提供给调用者之前对序列中的每个项目应用一些操作时很有用,但 您不想完成所有这些工作-front 并以数组或List&lt;T&gt; 的形式返回聚合结果。

        yield return 使这个延迟执行易于编写,因为生成的迭代器块仅在请求时将任何此类操作应用于每个项目。

        编辑:抱歉 - 我错过了您的代码中的一个要点(枚举基本字段),但我会留下这个答案,因为我认为它可能还是有用的。 Lasse Karlsen 的回答将满足您的需求,但除非您拥有庞大的类型层次结构,否则您并没有真正获得任何优势,而不仅仅是聚合结果并将它们返回到数组中。

        【讨论】:

        • 但这不会枚举基本字段。
        • 如果不使用DeclaredOnly会怎样呢?我现在不在我的电脑前,我的 iPad 没有运行 .NET 代码,所以我无法检查。
        • 使用 BindingFlags.FlattenHierarchy(根据记忆,可能有点不同)
        • GetFields(...) 有一个“特性”,其中不返回基类的私有字段。这是我的工作。
        【解决方案5】:

        您正在寻找的是“整体收益”。您必须遍历并返回从 GetFields 返回的每个项目,然后将其设置为基类。

        这不应该是必要的,因为有一个是否包含基类定义的标志。

        【讨论】:

          【解决方案6】:

          Type.GetFields 返回一个 FieldInfo 对象数组。

          由于您没有返回数组集合,因此您必须遍历数组并返回其中的每个对象,有点像这样:

          foreach (var fi in objectType.GetFields(...))
              yield return fi;
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-02-24
            • 2010-09-07
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多