2008-12-30 105 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); 
    } 
}