2008-12-30 113 views
25

我正在處理代碼生成問題,並遇到了與泛型相關的問題。以下是導致我遇到問題的「簡化」版本。如何使用反射獲得正確的文本定義的泛型類型?

Dictionary<string, DateTime> dictionary = new Dictionary<string, DateTime>(); 
string text = dictionary.GetType().FullName; 

與上面的代碼片斷text值如下:(換行增加了更好的可讀性)

System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=2.0.0.0, 
Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.DateTime, mscorlib, 
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] 

有沒有辦法讓類型名稱( type)以不同的格式解析上面的字符串?我希望以下結果爲text

System.Collections.Generic.Dictionary<System.String, System.DateTime> 
+2

請注意,如果您刪除`.FullName`和使用'的ToString()`不是,你得到的「文本「`System.Collections.Generic.Dictionary`2 [System.String,System.DateTime]``這是更具可讀性,並接近你想要的。 – 2013-10-01 12:39:31

回答

27

沒有內置的方式來獲得在.NET框架此表示。這是因爲沒有辦法讓它正確。有許多不能用C#樣式語法表示的構造。例如「<> foo」是IL中的有效類型名稱,但不能用C#表示。

但是,如果你正在尋找一個非常好的解決方案,它可以很快手工實現。以下解決方案適用於大多數情況。它不會處理

  1. 嵌套類型
  2. 非法C#名稱
  3. 夫婦的其他情形

例子:

public static string GetFriendlyTypeName(Type type) { 
    if (type.IsGenericParameter) 
    { 
     return type.Name; 
    } 

    if (!type.IsGenericType) 
    { 
     return type.FullName; 
    } 

    var builder = new System.Text.StringBuilder(); 
    var name = type.Name; 
    var index = name.IndexOf("`"); 
    builder.AppendFormat("{0}.{1}", type.Namespace, name.Substring(0, index)); 
    builder.Append('<'); 
    var first = true; 
    foreach (var arg in type.GetGenericArguments()) 
    { 
     if (!first) 
     { 
      builder.Append(','); 
     } 
     builder.Append(GetFriendlyTypeName(arg)); 
     first = false; 
    } 
    builder.Append('>'); 
    return builder.ToString(); 
} 
+0

超越!這完美的作品!謝謝!! – 2008-12-31 13:59:16

+0

如果你有機會編輯你的答案,並在代碼塊中包含「靜態字符串GetFriendlyTypeName(Type type){if(type.IsGenericParameter){return type.Name;}」) – 2008-12-31 14:39:23

0

我不認爲.NET有任何內置的,將做到這一點,所以你必須自己做。我認爲反射類提供了足夠的信息來重建這種形式的類型名稱。

+0

我已經試過這個路徑,你能詳細解釋一下嗎?我發現沒有給我提供這些信息的屬性或集合,我可以用它來重建聲明。 – 2008-12-30 22:38:53

0

我相信你可以通過

System.String,mscorlib程序,版本= 2.0.0.0,文化=中性公鑰= b77a5c561934e089

Type.Parse()。我認爲這是一個完全合格的類型名稱。

+0

不需要。原始的Dictionary.GetType()已經包含了一系列可以直接訪問的類型參數。不需要解析來獲得相同的結果。 – 2008-12-30 22:28:29

2
string text = dictionary.ToString(); 

提供你所要求的幾乎什麼:

System.Collections.Generic.Dictionary`2[System.String,System.DateTime] 
+0

雖然這不是一個通用的解決方案。任何使用.ToString()的類型都會打破這個解決方案 – JaredPar 2008-12-31 00:32:44

+0

@JaredPar這是通過使用`dictionary.GetType()。ToString()`來解決的。 – 2013-10-01 12:40:23

4

今天晚上我被玩弄了一下與擴展方法我試圖找到你的問題的答案。結果如下:這是一個沒有保修的代碼。 ;-)

internal static class TypeHelper 
{ 
    private const char genericSpecialChar = '`'; 
    private const string genericSeparator = ", "; 

    public static string GetCleanName(this Type t) 
    { 
     string name = t.Name; 
     if (t.IsGenericType) 
     { 
      name = name.Remove(name.IndexOf(genericSpecialChar)); 
     } 
     return name; 
    } 

    public static string GetCodeDefinition(this Type t) 
    { 
     StringBuilder sb = new StringBuilder(); 
     sb.AppendFormat("{0}.{1}", t.Namespace, t.GetCleanName()); 
     if (t.IsGenericType) 
     { 
      var names = from ga in t.GetGenericArguments() 
         select GetCodeDefinition(ga); 
      sb.Append("<"); 
      sb.Append(string.Join(genericSeparator, names.ToArray())); 
      sb.Append(">"); 
     } 
     return sb.ToString(); 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     object[] testCases = { 
           new Dictionary<string, DateTime>(), 
           new List<int>(), 
           new List<List<int>>(), 
           0 
          }; 
     Type t = testCases[0].GetType(); 
     string text = t.GetCodeDefinition(); 
     Console.WriteLine(text); 
    } 
} 
19

良好和清潔的替代,由於@LukeH's comment,是

using System; 
using System.CodeDom; 
using System.Collections.Generic; 
using Microsoft.CSharp; 
//... 
private string GetFriendlyTypeName(Type type) 
{ 
    using (var p = new CSharpCodeProvider()) 
    { 
     var r = new CodeTypeReference(type); 
     return p.GetTypeOutput(r); 
    } 
} 
相關問題