Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
471 views
in Technique[技术] by (71.8m points)

c# - Type.GetType fails when invoked as method group but not in lambda expression

The following example shows Type.GetType failing in a specific scenario.

GetType succeeds when I provide it the class name string (including namespace) in a lambda expression, but fails when I specify the call to GetType as a method group.

Fails:

collectionOfClassNames.Select(GetType)

Succeeds:

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

Both methods succeed when the class path includes the assembly name. I suspect it's something to do with the current context/scope given the IL generated for the above. I can see the differences in the IL but I still can't explain the exact cause.

The below is a runnable example that demonstrates the problem.

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();
        }
    }
}

I'd like to know why #2 fails but #3 succeeds.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Am not 100% sure, I may be wrong.. I'll propose what I've examined.

Version 2 gets compiled into a func delegate like this new Func<string, Type>(Type.GetType)

Version 3 gets compiled into a compiler generated method in your same class something like this

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

and to a func new Func<string, Type>(Program.<Main>b__0)

So, While executing your enumerator Version2 is just a func which will be invoked my WhereSelectArrayIterator<TSource, TResult> private class lives in System.Core.dll

Where as Version3 lives in your assembly.

Coming to the point. If Type.GetType is invoked with partial names(without fully qualified name) It doesn't knows which assembly the type resides, It gets the calling assembly and assumes the type lives there.

Hence Version3 lives in your assembly Type.GetType figured out your type's assembly and scans the assembly fully returns the correct type.

But this is not the case in Version2. You're not actually invoking the Type.GetType there. It is being invoked by WhereSelectArrayIterator... class which is in System.Core.dll. So this assumes your type lives in System.Core.dll and Type.GetType fails to find out your type.


Edit: Following snippet proves above statements were correct

We fake a class in our assembly and name it System.Linq.Expressions.Expression to see the behavior.

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
    {

    }
}

Outputs

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]

Hope this helps


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...