从C#关键字获取正确类型名称的最佳方法是什么?

时间:2009-11-03 00:36:16

标签: c#

我想使用Type.GetType(nameOfTheType)创建一个类型,其中nameOfTheType是一些字符串。如果nameOfTheType是“System.String”,这很好用。如果nameOfTheType是“string”,则抛出异常。所有带有关键字快捷键的类型都会失败。

除了一个大的switch语句之外,还有一种方法可以将所有带有关键字快捷键的类型正确地映射到它们的实际类型名称(根据这个列表:http://msdn.microsoft.com/en-us/library/ya5y69ds%28VS.80%29.aspx)?

编辑:Gabriel正确地指出我链接的MSDN页面不包含可空类型(int?等),但重要的是可以正确映射可空类型。

6 个答案:

答案 0 :(得分:5)

<强>声明
首先我要说的是,我将在这里展示的方法纯粹是出于教育目的。请使用生产代码中其他答案中演示的开关或字典查找。

<强>答案
因此,找出编译器如何解释给定代码片段的唯一方法是实际编译它并检查生成的程序集。这在.NET中并不难做到......您可以在运行时使用C#编译器编译任意字符串,如下所示:

private static Type GetTypeByFullNameOrAlias(string typeName)
{
  Type type = Type.GetType(typeName);

  if (type == null)
  {
    using (var provider = new CSharpCodeProvider())
    {
      var compiler = provider.CompileAssemblyFromSource(
        new CompilerParameters
          {GenerateInMemory = true, GenerateExecutable = false, IncludeDebugInformation = false},
        "public class A { public " + typeName + " B; }");

      type = ((FieldInfo)compiler.CompiledAssembly.GetType("A").GetMember("B")[0]).FieldType;
    }
  }

  return type;
}

然而,这种技术有几个缺点......首先它很慢。当然,您可以缓存结果并执行其他技巧来加速该过程。但是还有另一个问题,那就是它每次到达“if”语句的内部时都会编译并加载一个完整的程序集。它加载的每个组件都不能再次卸载,并且会在应用程序关闭之前一直挂起,因此这种技术也会泄漏少量内存。同样,您可以将生成的程序集加载到单独的AppDomain中,并在检查完类型后卸载域,但这样只会使代码变慢。

但正如我在开始时所说的那样,只需在生产代码中使用“switch”解决方案......它可以很好地翻译别名。而且它们不会很快就会改变那些语言别名。

答案 1 :(得分:4)

您可以使用CodeDom创建CodeVariableDeclarationStatement,并检索Type属性:

var stmt = new CodeVariableDeclarationStatement("string", "test");
string systemTypeName = stmt.Type.BaseType;

我不确定您是否可以独立使用CodeDom类,但它应该是从“string”到“System.String”的简单方法,而无需使用 switch

编辑:

考虑更多,可能可以直接使用CodeTypeReference,并快捷上述流程:

var systemTypeName = new CodeTypeReference("string").BaseType;

CodeVariableDeclarationStatement.Type属性是CodeTypeReference。通过直接使用CodeTypeReference,您不需要使用虚拟变量名称,它可以成为一个单行。

工作示例:

我找到了之前执行此操作的代码。它比我希望的要复杂一点,但它完成了工作,如果生成的类被缓存,初始编译后性能良好:

using System;
using System.CodeDom;
using System.Collections.Generic;
using System.CodeDom.Compiler;

using Microsoft.CSharp;
using System.Diagnostics;

namespace ConsoleApplication1
{
    public interface IDynamicTypeNameMapper
    {
        string GetTypeName();
    }

    class Program
    {
        static readonly string[] csharpKeywords = new[]
        {
            "byte",
            "short",
            "int",
            "long",
            "float",
            "double",
            "string"
        };

        static Dictionary<string, IDynamicTypeNameMapper> s_mappers;

        static void Main(string[] args)
        {
            s_mappers = new Dictionary<string, IDynamicTypeNameMapper>();

            var provider = new CSharpCodeProvider();
            var options = new CompilerParameters();
            options.ReferencedAssemblies.Add("ConsoleApplication1.exe");
            options.GenerateInMemory = true;

            var stopwatch = new Stopwatch();
            stopwatch.Start();
            foreach (string keyword in csharpKeywords)
            {
                string className = "DynamicTypeNameMapper_" + keyword;
                string literal = "using System; using ConsoleApplication1; namespace Test { public class " + className + ": IDynamicTypeNameMapper { public string GetTypeName() { return typeof(" + keyword + ").FullName; } } }";
                var snippet = new CodeSnippetCompileUnit(literal);

                var results = provider.CompileAssemblyFromDom(options, snippet);

                var typeNameMapper = results.CompiledAssembly.CreateInstance("Test." + className) as IDynamicTypeNameMapper;
                if (typeNameMapper != null)
                {
                    s_mappers.Add(keyword, typeNameMapper);
                    Console.WriteLine(typeNameMapper.GetTypeName());
                }
            }
            stopwatch.Stop();
            Console.WriteLine("Inital time: " + stopwatch.Elapsed.ToString());

            stopwatch.Reset();
            stopwatch.Start();

            for (int i = 0; i < 1000; i++)
            {
                foreach (string keyword in csharpKeywords)
                {
                    s_mappers[keyword].GetTypeName();
                }
            }
            stopwatch.Stop();

            Console.WriteLine("Cached time: " + stopwatch.Elapsed.ToString());

            Console.ReadLine();
        }
    }
}

此应用的输出如下:

System.Byte
System.Int16
System.Int32
System.Int64
System.Single
System.Double
System.String
Inital time: 00:00:00.3090559
Cached time: 00:00:00.0011934

答案 2 :(得分:2)

System.String 类型名称,'string'是别名。以下代码适用于我,所以也许我没有得到你的用例:

string s = "";
Type t = Type.GetType( s.GetType( ).ToString( ) );
Console.WriteLine( t );  // prints "System.String"

...当然这完全是多余的,因为你可以问'''的类型:)

编辑: 看到下面的评论后,您可能只需要进行切换。问题是Type.GetType()需要一个完全限定的名称,并且(在一些搜索之后)我找不到将别名与其类型名称匹配的方法。如果有人确实发现了这一点,那就太棒了,但这样的事情会运作得很好:

switch (userType)
{
    case "string": return "System.String";
    case "sbyte": return "System.SByte";
    case "byte": return "System.Byte";
    case "short": return "System.Int16";
    case "ushort": return "System.UInt16";
    case "int": return "System.Int32";
    case "uint": return "System.UInt32";
    case "long": return "System.Int64";
    case "ulong": return "System.UInt64";
    case "char": return "System.Char";
    case "float": return "System.Single";
    case "double": return "System.Double";
    case "bool": return "System.Boolean";
    case "decimal": return "System.Decimal";
    case "void": return "System.Void";
    case "object": return "System.Object";
    default: return userType;
}

答案 3 :(得分:2)

stringint以及其他所有关键字都是:关键字。它只是一直写StringInt32Double的捷径。你只需要使用他们的完全限定名字,但我不知道为什么你反对简单地使用他们的全名,而不是他们的关键词。

我看到将关键字映射到完全限定名称的唯一方法就是switch语句。我知道没有任何方法能够满足您的要求。

答案 4 :(得分:1)

static readonly Dictionary<String, String> types = new Dictionary<String, String>()
{
    { "string", "System.String" },
    { "sbyte", "System.SByte" },
    { "byte", "System.Byte" },
    { "short", "System.Int16" },
    { "ushort", "System.UInt16" },
    { "int", "System.Int32" },
    { "uint", "System.UInt32" },
    { "long", "System.Int64" },
    { "ulong", "System.UInt64" },
    { "char", "System.Char" },
    { "float", "System.Single" },
    { "double", "System.Double" },
    { "bool", "System.Boolean" },
    { "decimal", "System.Decimal" },
    { "void", "System.Void" },
    { "object", "System.Object" }
};

private void Execute(String user_type)
{
    String type;
    if (!types.TryGetValue(user_type, out type))
    {
        type = user_type;
    }
}

答案 5 :(得分:-1)

Type name字符串来自某个地方,如果某个地方可以使用Type类的.FullName属性以下列方式强制创建Type name字符串,那么您将无法获得您指示的简短表单有问题:

string obj = ""; // using obj as an example - could be of any data type
string bestTypeName = obj.GetType().FullName; // produces "System.String"