13

我想获得一个指定(原始)类型的C# 友好名称System.Type的给定 a ,基本上是 C# 编译器在读取 C# 源代码时所做的方式。string

我觉得描述我所追求的最好方式是单元测试的形式。

我希望存在一种通用技术,可以使以下所有断言都通过,而不是尝试对特殊 C# 名称的特殊情况进行硬编码。

Type GetFriendlyType(string typeName){ ...??... }

void Test(){
    // using fluent assertions

    GetFriendlyType( "bool" ).Should().Be( typeof(bool) );
    GetFriendlyType( "int" ).Should().Be( typeof(int) );

    // ok, technically not a primitive type... (rolls eyes)
    GetFriendlyType( "string" ).Should().Be( typeof(string) ); 

    // fine, I give up!
    // I want all C# type-aliases to work, not just for primitives
    GetFriendlyType( "void" ).Should().Be( typeof(void) );
    GetFriendlyType( "decimal" ).Should().Be( typeof(decimal) ); 

    //Bonus points: get type of fully-specified CLR types
    GetFriendlyName( "System.Activator" ).Should().Be(typeof(System.Activator));

    //Hi, Eric Lippert! 
    // Not Eric? https://stackoverflow.com/a/4369889/11545
    GetFriendlyName( "int[]" ).Should().Be( typeof(int[]) ); 
    GetFriendlyName( "int[,]" ).Should().Be( typeof(int[,]) ); 
    //beating a dead horse
    GetFriendlyName( "int[,][,][][,][][]" ).Should().Be( typeof(int[,][,][][,][][]) ); 
}

到目前为止我尝试了什么:

这个问题是我的一个旧问题的补充,询问如何从类型中获取“友好名称”。

这个问题的答案是:使用CSharpCodeProvider

using (var provider = new CSharpCodeProvider())
{
    var typeRef = new CodeTypeReference(typeof(int));
    string friendlyName = provider.GetTypeOutput(typeRef);
}

我无法弄清楚如何(或如果可能的话)以相反的方式进行操作并从中获取实际的 C# 类型CodeTypeReference(它也有一个接受 a 的 ctor string

var typeRef = new CodeTypeReference(typeof(int));
4

5 回答 5

11

你不是已经完成了大部分吗?

下面根据http://msdn.microsoft.com/en-us/library/ya5y69ds.aspx为您提供所有内置 C# 类型,以及void.

using Microsoft.CSharp;
using System;
using System.CodeDom;
using System.Reflection;

namespace CSTypeNames
{
    class Program
    {
        static void Main(string[] args)
        {
            // Resolve reference to mscorlib.
            // int is an arbitrarily chosen type in mscorlib
            var mscorlib = Assembly.GetAssembly(typeof(int));

            using (var provider = new CSharpCodeProvider())
            {
                foreach (var type in mscorlib.DefinedTypes)
                {
                    if (string.Equals(type.Namespace, "System"))
                    {
                        var typeRef = new CodeTypeReference(type);
                        var csTypeName = provider.GetTypeOutput(typeRef);

                        // Ignore qualified types.
                        if (csTypeName.IndexOf('.') == -1)
                        {
                            Console.WriteLine(csTypeName + " : " + type.FullName);
                        }
                    }
                }
            }

            Console.ReadLine();
        }
    }
}

这是基于我认为在撰写本文时正确的几个假设:

  • 所有内置 C# 类型都是 mscorlib.dll 的一部分。
  • 所有内置 C# 类型都是System命名空间中定义的类型的别名。
  • 只有调用返回的内置 C# 类型的名称CSharpCodeProvider.GetTypeOutput没有单个 '.' 在他们中。

输出:

object : System.Object
string : System.String
bool : System.Boolean
byte : System.Byte
char : System.Char
decimal : System.Decimal
double : System.Double
short : System.Int16
int : System.Int32
long : System.Int64
sbyte : System.SByte
float : System.Single
ushort : System.UInt16
uint : System.UInt32
ulong : System.UInt64
void : System.Void

现在我只需要坐等埃里克来告诉我我了。我已经接受了我的命运。

于 2013-06-07T13:07:19.107 回答
6

'int'、'bool' 等别名不是 .NET Framework 的一部分。在内部,它们被转换为 System.Int32、System.Boolean 等。Type.GetType("int") 应该返回 null。解决此问题的最佳方法是使用字典来映射别名及其类型,例如

        Dictionary<string, Type> PrimitiveTypes = new Dictionary<string, Type>();
        PrimitiveTypes.Add("int", typeof(int));
        PrimitiveTypes.Add("long", typeof(long));
        etc.etc..
于 2013-06-07T12:42:04.197 回答
5

这是使用Roslyn的一种方法:

using System;
using System.Linq;
using Roslyn.Scripting.CSharp;

namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(GetType("int[,][,][][,][][]"));
            Console.WriteLine(GetType("Activator"));
            Console.WriteLine(GetType("List<int[,][,][][,][][]>"));
        }

        private static Type GetType(string type)
        {
            var engine = new ScriptEngine();
            new[] { "System" }
                .ToList().ForEach(r => engine.AddReference(r));
            new[] { "System", "System.Collections.Generic" }
                .ToList().ForEach(ns => engine.ImportNamespace(ns));
            return engine
                .CreateSession()
                .Execute<Type>("typeof(" + type + ")");
        }
    }
}
于 2013-06-07T15:01:27.680 回答
3

这是一种方法:

public Type GetType(string friendlyName)
{
    var provider = new CSharpCodeProvider();

    var pars = new CompilerParameters
    {
        GenerateExecutable = false,
        GenerateInMemory = true
    };

    string code = "public class TypeFullNameGetter"
                + "{"
                + "     public override string ToString()"
                + "     {"
                + "         return typeof(" + friendlyName + ").FullName;"
                + "     }"
                + "}";

    var comp = provider.CompileAssemblyFromSource(pars, new[] { code });

    if (comp.Errors.Count > 0)
        return null;

    object fullNameGetter = comp.CompiledAssembly.CreateInstance("TypeFullNameGetter");
    string fullName = fullNameGetter.ToString();            
    return Type.GetType(fullName);
}

然后,如果你传入“int”、“int[]”等,你会得到相应的类型。

于 2013-06-07T13:31:03.053 回答
0

这是我的尝试。我使用两个类似的库来处理它:

  • Mono C# 编译器即服务
  • MS Roslyn C# 编译器

我认为它非常干净和直接。

这里我使用的是 Mono 的 C# 编译器即服务,即Mono.CSharp.Evaluator类。(不出所料,它以Nuget 包的形式提供,命名为Mono.CSharp

using Mono.CSharp;

    public static Type GetFriendlyType(string typeName)
    {
        //this class could use a default ctor with default sensible settings...
        var eval = new Mono.CSharp.Evaluator(new CompilerContext(
                                                 new CompilerSettings(),
                                                 new ConsoleReportPrinter()));

        //MAGIC! 
        object type = eval.Evaluate(string.Format("typeof({0});", typeName));

        return (Type)type;
    }

下一个:来自“ 41 号楼​​的朋友”又名罗斯林的对手...

之后:

Roslyn 几乎同样易于安装 - 一旦你弄清楚什么是什么。我最终使用了Nuget 包“Roslyn.Compilers.CSharp”(或者将其作为VS 插件获取)。请注意,Roslyn需要.NET 4.5 项目。

代码更简洁:

using Roslyn.Scripting.CSharp;

    public static Type GetFriendlyType(string typeName)
    {
        ScriptEngine engine = new ScriptEngine();
        var type = engine.CreateSession()
                         .Execute<Type>(string.Format("typeof({0})", typeName));
        return type;
    }
于 2013-06-07T14:11:29.723 回答