簡體   English   中英

從C#關鍵字獲取正確類型名稱的最佳方法是什么?

[英]What is the best way to get a proper type name from a C# keyword?

我想使用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?等),但重要的是可以正確映射可空類型。

放棄
首先我要說的是,我將在這里展示的方法純粹是出於教育目的。 請使用生產代碼中其他答案中演示的開關或字典查找。

回答
因此,找出編譯器如何解釋給定代碼片段的唯一方法是實際編譯它並檢查生成的程序集。 這在.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”解決方案......它在翻譯別名方面做得非常好。 並不是說他們很快就會改變那些語言別名。

您可以使用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

System.String 類型名稱,'string'是別名。 以下代碼適用於我,所以也許我沒有得到你的用例:

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

...這當然是完全多余的,因為你可以問's'的類型:)

編輯:看到下面的評論后,您可能只需要進行切換。 問題是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;
}

stringint和其他所有關鍵字就是: 關鍵字 它只是一直編寫StringInt32Double的快捷方式。 你只需要使用他們的完全限定名稱,但我不知道為什么你反對簡單地使用他們的全名,而不是他們的關鍵字。

我看到將關鍵字映射到完全限定名稱的唯一方法就是switch語句。 沒有任何方法可以讓我知道你會做什么。

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

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"

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM