【问题标题】:Combine two LINQ queries?结合两个 LINQ 查询?
【发布时间】:2008-12-10 12:22:10
【问题描述】:

我想我有一个心理障碍,但有人可以告诉我如何将这两个 LINQ 语句合并为一个吗?

/// <summary>
/// Returns an array of Types that implement the supplied generic interface in the
/// current AppDomain.
/// </summary>
/// <param name="interfaceType">Type of generic interface implemented</param>
/// <param name="includeAbstractTypes">Include Abstract class types in the search</param>
/// <param name="includeInterfaceTypes">Include Interface class types in the search</param>
/// <returns>Array of Types that implement the supplied generic interface</returns>
/// <remarks>
/// History.<br/>
/// 10/12/2008      davide       Method creation.<br/>
/// </remarks>
public static Type[] GetTypesImplementingGenericInterface(Type interfaceType, bool includeAbstractTypes, bool includeInterfaceTypes)
{
    // Use linq to find types that implement the supplied interface.
    var allTypes = AppDomain.CurrentDomain.GetAssemblies().ToList()
                    .SelectMany(s => s.GetTypes())
                    .Where(p => p.IsAbstract == includeAbstractTypes  
                                    && p.IsInterface == includeInterfaceTypes);

    var implementingTypes = from type in allTypes
                            from intf in type.GetInterfaces().ToList()
                            where intf.FullName != null && intf.FullName.Contains(interfaceType.FullName)
                            select type;

    return implementingTypes.ToArray<Type>();
}

我正在避免 IsAssignableFrom,因为它在不提供通用接口的特定类型时似乎会​​失败,因此我相信在 IsAssignableFrom 上使用 FullName caparison 就足够了,例如:

namespace Davide
{
    interface IOutput<TOutputType> { }

    class StringOutput : IOutput<string> { }
}

typeof(IOutput).FullName 将返回“Davide+IOutput`1”

typeof(StringOutput).GetInterfaces()[0].FullName 将返回“Davide+IOutput`1[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]”

因此使用 FullName.Contains 就足够了

【问题讨论】:

    标签: c# linq


    【解决方案1】:

    SelectMany 转换为第二个“来自”:

    var implementors = from assembly in AppDomain.CurrentDomain.GetAssemblies()
                       from type in assembly.GetTypes()
                       where type.IsAbstract == includeAbstractTypes
                       where type.IsInterface == includeInterfaceTypes
                       from intf in type.GetInterfaces()
                       where intf.FullName != null && 
                             intf.FullName.Contains(interfaceType.FullName)
                       select type;
    

    为了主观清楚,我将条件分成多个“where”子句,顺便说一句。

    这可以编译,但我没有测试它是否真的有效:) 正如另一个答案所示,您可以将“Any”与 GetInterfaces() 一起使用,而不是最终的“from”子句。

    请注意,无需到处调用 ToList() - LINQ 旨在能够处理整个序列。

    顺便说一句,我不确定你为什么要通过 type.GetInterfaces() 来检查。与使用 Type.IsAssignableFrom 相比,有什么不同(和可取的)吗?这样会更简单:

    var implementors = from assembly in AppDomain.CurrentDomain.GetAssemblies()
                       from type in assembly.GetTypes()
                       where type.IsAbstract == includeAbstractTypes
                       where type.IsInterface == includeInterfaceTypes
                       where interfaceType.IsAssignableFrom(type)
                       select type;
    

    您实际上在某个不同的程序集中有相同的接口类型名称吗?

    【讨论】:

    • 我最初有 IsAssignableFrom 但发现当您提供没有指定类型的通用接口时,例如typeof(IInterface),它会失败,因为实际的类实现了特定类型,例如 ConcreteClass : IInterface,因此它们不匹配。
    • @Davide:那样按名称比较也行不通。如果你认为是这样,请举一个完整的例子:)
    • @Jon:我用一个例子更新了这个问题(希望它有意义),如果你认为我在全名比较中遗漏了一些东西,请告诉我。老实说,这不是我的首选方法,但我还不确定是否有替代方法。
    • 啊,我错过了您使用的是 FullName.Contains,而不是 FullName ==。就个人而言,我会编写一个单独的方法来“正确”执行此操作(使用 GetGenericTypeDefinition),但如果您对 FullName 感到满意,那么上面的第一个查询应该可以工作。
    • 乔恩,我已经用最终解决方案发布了我自己的答案,我找到了我的替代方案(耶),并决定按照讨论使用 GetGenericTypeDefinition。感谢它帮助的讨论。
    【解决方案2】:

    这样可以吗:

        public static Type[] GetTypesImplementingGenericInterface(Type interfaceType, bool includeAbstractTypes, bool includeInterfaceTypes)
        {
            // Use linq to find types that implement the supplied interface.
            var allTypes = AppDomain.CurrentDomain.GetAssemblies().ToList()
                                        .SelectMany(s => s.GetTypes())
                                        .Where(p => p.IsAbstract == includeAbstractTypes
                                               && p.IsInterface == includeInterfaceTypes
                                               && p.GetInterfaces().Any(i=>i.FullName != null && i.FullName.Contains(interfaceType.FullName))
                                               );
    
            //var implementingTypes = from type in allTypes
            //                        from intf in type.GetInterfaces().ToList()
            //                        where intf.FullName != null && intf.FullName.Contains(interfaceType.FullName)
            //                        select type;
    
            //return implementingTypes.ToArray<Type>();
    
            return allTypes.ToArray();
        }
    

    【讨论】:

      【解决方案3】:

      我可以提出其他解决方案吗?

          public static Type[] GetTypesImplementingGenericInterface(Type interfaceType, bool includeAbstractTypes, bool includeInterfaceTypes)
          {
              // Use linq to find types that implement the supplied interface.
              var implementingTypes = AppDomain.CurrentDomain.GetAssemblies()
                                          .SelectMany(s => s.GetTypes())
                                          .Where(p => interfaceType.IsAssignableFrom(p)
                                                    && (
                                                           (p.IsAbstract && includeAbstractTypes) 
                                                           || (p.IsInterface && includeInterfaceTypes)
                                                           || (!p.IsAbstract && !p.IsInterface)
                                                       )
                                                );
      
              return implementingTypes.ToArray<Type>();
          }
      

      【讨论】:

      • 谢谢布鲁诺,看看我在 Jon Skeet 的帖子上发表的关于 IsAssignableFrom 的评论,你可能会感兴趣。
      【解决方案4】:

      在与 Jon Skeet 进行简短讨论并进行更多思考后,我发布了以下答案。我更改了使用 GetGenericTypeDefinition 而不是 FullName.Contains 的方法,这将是一个更强大的解决方案。我还更改了 IsAbstract 和 IsInterface 的 LINQ 查询 Where 子句,因为它们没有按预期排除类型。感谢大家的反馈。

      /// <summary>
      /// Returns an array of Types that implement the supplied generic interface in the
      /// current AppDomain.
      /// </summary>
      /// <param name="interfaceType">Type of generic interface implemented</param>
      /// <param name="excludeAbstractTypes">Exclude Abstract class types in the search</param>
      /// <param name="excludeInterfaceTypes">Exclude Interface class types in the search</param>
      /// <returns>Array of Types that implement the supplied generic interface</returns>
      /// <remarks>
      /// History.<br/>
      /// 11/12/2008      davide       Created method.<br/>
      /// 11/12/2008      davide       Altered method to use a two LINQ query pass.<br/>
      /// 11/12/2008      davide       Altered method to use optimised combined LINQ query.<br/>
      /// 12/12/2008      davide       Altered method and replaced FullName criteria match with GetGenericTypeDefinition.<br/>
      /// </remarks>
      public static Type[] GetTypesImplementingGenericInterface(Type interfaceType, bool excludeAbstractTypes, bool excludeInterfaceTypes)
      {
          if (!interfaceType.IsGenericType)
          {
              throw new ArgumentException("Supplied interface is not a Generic type");
          }
      
          if (interfaceType.ContainsGenericParameters)
          {
              interfaceType = interfaceType.GetGenericTypeDefinition();
          }
      
          // Use linq to find types that implement the supplied generic interface.
          var implementingTypes = from assembly in AppDomain.CurrentDomain.GetAssemblies()
                                  from type in assembly.GetTypes()
                                  where (type.IsAbstract != excludeAbstractTypes) || (!excludeAbstractTypes)
                                  where (type.IsInterface != excludeInterfaceTypes) || (!excludeInterfaceTypes)
                                  from intf in type.GetInterfaces()
                                  where intf.IsGenericType && intf.GetGenericTypeDefinition() == interfaceType
                                  select type;
      
          return implementingTypes.ToArray<Type>();
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-24
        相关资源
        最近更新 更多