linq puzzle ...等效语法......不等同于结果!

时间:2009-09-04 18:39:32

标签: c# linq

在代码中只存在一种实现IResourceConverter的类型。这就是以下两个linq语句所寻求的内容。前者没有找到它。后者确实如此。但是它们都是等效的语法(或者至少应该是!)。

Linq声明1:

List<Type> toInstantiate = AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(assembly => assembly.GetTypes())
    .Where(type => typeof(IResourceConverter).IsAssignableFrom(type) 
        && type != typeof(IResourceConverter))
    .ToList();

这会返回0结果。

Linq声明2:

除了where子句之外,我已经保留了linq的完整性,我爆发并使用foreach循环执行等效操作

List<Type> toInstantiate = new List<Type>();            
List<Type> allTypes = AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(assembly => assembly.GetTypes())
    .ToList();

foreach (Type t in allTypes)
{
    if (typeof(IResourceConverter).IsAssignableFrom(t) 
        && t != typeof(IResourceConverter))
    toInstantiate.Add(t);
}

在这种情况下,forInstantiate有1个结果......正是我所期望的。

对这种奇怪行为的任何解释?

4 个答案:

答案 0 :(得分:2)

这也似乎产生了正确的结果,但我不确定原因。

List<Type> allTypes = AppDomain.CurrentDomain
    .GetAssemblies().SelectMany(assembly => assembly.GetTypes())
    .ToList();

List<Type> toInstantiate = allTypes
    .Where(type => typeof(IList).IsAssignableFrom(type) && type != typeof(IList))
    .ToList();

foreach,此查询与原始查询之间的唯一区别是原始查询是延迟评估的。为什么这会有所不同,因为类型是静态的我不确定。

答案 1 :(得分:2)

运行以下程序,并使用diff工具比较文件a.txtb.txt

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.IO;
using System.Reflection;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var x = Foo().OrderBy(t => t.FullName).Select(t => t.FullName);
            var y = Bar().OrderBy(t => t.FullName).Select(t => t.FullName);

            File.WriteAllLines("a.txt", x.ToArray());
            File.WriteAllLines("b.txt", y.ToArray());
            Console.ReadKey();
        }

        private static List<Assembly> Foo()
        {
            List<Type> toInstantiate = AppDomain.CurrentDomain
                .GetAssemblies().SelectMany(assembly => assembly.GetTypes())
                .ToList();

            return toInstantiate.Select(t => t.Assembly).Distinct().ToList();
        }

        private static List<Assembly> Bar()
        {
            List<Type> toInstantiate = new List<Type>();
            List<Type> allTypes = AppDomain.CurrentDomain
                .GetAssemblies().SelectMany(assembly => assembly.GetTypes())
                .ToList();

            foreach (Type t in allTypes)
            {
                toInstantiate.Add(t);
            }

            return toInstantiate.Select(t => t.Assembly).Distinct().ToList();
        }
    }
}

我注意到两个代码可以看到的程序集有很大的不同。也就是说,第二个函数Bar可以看到基于linq的组件不能。

更有趣的是,如果我反转执行顺序,现在Foo可以看到另一个不能执行的程序集,即完全相反。

最后,如果我运行第一个查询两次,输出是相同的,所以:

    Foo, Foo, Bar
    Foo, Bar, Foo
    Bar, Bar, Foo
    Bar, Foo, Bar

所有产生相同的结果。

所以我唯一的假设是一些程序集被一个查询加载而另一个查询不会导致加载。

答案 2 :(得分:0)

我不是真正的LINQ专家,但我可以冒险猜测;这看起来类似于我在Hibernate(Java ORM工具)中遇到的问题:

在Hibernate中,可以将Collection属性设置为懒惰地初始化。当属性未初始化时,Hibernate使用字节码检测来生成代理,通过子类化Parent并添加自己的行为来执行延迟加载操作。如果我有类似的课程:

class Test {
 Collection<Parent> getEntities() //lazy
}

class Parent extends Child {
}

class Child {
}

我可以调用getEntities()来返回Parent对象的集合。由于我的getEntities被标记为lazy,因此我返回的对象会自动生成Parent的子类。即使集合中的一个项目代表一个Child,一个像“myEntity instanceof Child”的检查也不起作用,因为实际的对象只是Child的代理,而不是真正的Child对象。

我知道LINQ是一种查询机制,可用于数据访问或对象。在你的情况下,你的where子句中的“type”对象可能是实际Type对象的某种查询代理,类似于上面描述的情况,所以isAssignable()确定代理对象没有实现IResourceConverter? / p>

答案 3 :(得分:0)

确保下面的表达式实际上计算为true(即将其从LINQ语句中取出,并直接根据类型本身进行评估):

bool doesImplIface = typeof(IResourceConverter).IsAssignableFrom(type) && type != typeof(IResourceConverter);

Where子句过滤结果的唯一原因是因为表达式没有评估为true。运行上面的代码行应该清楚地表明你试图执行的表达式是否评估你认为它的方式。如果没有,请调整它,直到获得适当的行为,并将新表达式放在LINQ语句中。