7

以下示例显示 Type.GetType 在特定场景中失败。

当我在 lambda 表达式中提供类名字符串(包括命名空间)时,GetType 成功,但当我将对 GetType 的调用指定为方法组时失败。

失败:

collectionOfClassNames.Select(GetType)

成功:

collectionOfClassNames.Select(s => GetType(s))

当类路径包含程序集名称时,这两种方法都会成功。考虑到为上述内容生成的 IL,我怀疑这与当前的上下文/范围有关。我可以看到 IL 中的差异,但我仍然无法解释确切的原因。

下面是一个演示问题的可运行示例。

using System;
using System.Linq;
using System.Reflection;

namespace GetTypeTest
{
    public class FindMe{}

    class Program
    {
        static void Main(string[] args)
        {
            var assemblyName = Assembly.GetExecutingAssembly().FullName;
            var className = "GetTypeTest.FindMe";
            var classAndAssembly = string.Format("{0}, {1}", className, assemblyName);

            // 1) GetType succeeds when input is "class, assembly", using method group
            var result = new[] { classAndAssembly }.Select(Type.GetType).ToArray();
            Console.WriteLine("1) Method group & class+assembly: {0}", result.First());

            // 2) GetType fails when input is just the class name, using method group
            var result2 = new[] { className }.Select(Type.GetType).ToArray();
            Console.WriteLine("2) Method group & class name only: {0}", result2.First());

            // 3) Identical to (2) except using lamba expression - this succeeds...
            var result3 = new[] { className }.Select(s => Type.GetType(s)).ToArray();
            Console.WriteLine("3) Lambda expression & class name only: {0}", result3.First());

            // 4) Method group and core type class name
            var result4 = new[] { "System.String" }.Select(Type.GetType).ToArray();
            Console.WriteLine("4) Method group for System.String: {0}", result4.First());

            Console.ReadLine();
        }
    }
}

我想知道为什么#2 失败但#3 成功。

4

1 回答 1

3

我不是 100% 肯定,我可能是错的。. 我将提出我检查过的内容。

版本 2 被编译成这样的 func 委托new Func<string, Type>(Type.GetType)

版本 3 被编译到同一个类中的编译器生成的方法中,如下所示

[CompilerGenerated]
private static Type <Main>b__0(string s)
{
    Type type;
    type = Type.GetType(s);
Label_0009:
    return type;
}

和一个函数new Func<string, Type>(Program.<Main>b__0)

因此,虽然执行您的枚举器Version2只是一个func将被调用的,但我的WhereSelectArrayIterator<TSource, TResult>私有类位于 System.Core.dll

Version3在您的程序集中 的位置。

说到重点。如果Type.GetType使用部分名称(没有完全限定名称)调用它不知道该类型驻留在哪个程序集中,它会获得调用assembly并假设该类型存在于那里。

因此, Version3存在于您的程序集中,Type.GetType找出您的类型的程序集并扫描程序集完全返回正确的类型。

但在Version2中情况并非如此。你实际上并没有调用Type.GetType那里。它被WhereSelectArrayIterator... classwhich is in调用System.Core.dll。所以这假设你的类型存在System.Core.dll并且Type.GetType无法找出你的类型。


编辑: 以下片段证明上述陈述是正确的

我们在程序集中伪造一个类并命名它System.Linq.Expressions.Expression以查看行为。

namespace GetTypeTest
{
    public class FindMe { }

    class Program
    {
        static void Main(string[] args)
        {
            var assemblyName = Assembly.GetExecutingAssembly().FullName;
            var className = "System.Linq.Expressions.Expression";//"GetTypeTest.FindMe";
            var classAndAssembly = string.Format("{0}, {1}", className, assemblyName);

            // 1) GetType succeeds when input is "class, assembly", using method group
            var result = new[] { classAndAssembly }.Select(Type.GetType).ToArray();
            Console.WriteLine("1) Method group & class+assembly: {0}, {1}", result.First(), result.First().Assembly);//your assembly

            // 2) GetType fails when input is just the class name, using method group
            var result2 = new[] { className }.Select(Type.GetType).ToArray();
            Console.WriteLine("2) Method group & class name only: {0}, {1}", result2.First(), result2.First().Assembly);//System.Core assembly

            // 3) Identical to (2) except using lamba expression - this succeeds...
            var result3 = new[] { className }.Select(s => Type.GetType(s)).ToArray();
            Console.WriteLine("3) Lambda expression & class name only: {0}, {1}", result3.First(), result3.First().Assembly);//your assembly

            // 4) Method group and core type class name
            var result4 = new[] { "System.String" }.Select(Type.GetType).ToArray();
            Console.WriteLine("4) Method group for System.String: {0}, {1}", result4.First(), result4.First().Assembly);//mscorlib assembly

            Console.ReadLine();
        }
    }
}

namespace System.Linq.Expressions
{
    public class Expression
    {

    }
}

输出

System.Linq.Expressions.Expression [your assembly]
System.Linq.Expressions.Expression [System.Core assembly] since WhereSelectArrayIterator.. belongs to System.Core assembly
System.Linq.Expressions.Expression [your assembly] since compiler generated method belongs to your assembly
System.Linq.Expressions.Expression [mscorlib assembly]

希望这可以帮助

于 2013-09-21T11:23:20.523 回答