2011-06-19 43 views
17

System.Type類的名稱屬性在泛型類型的情況下返回一個奇怪的結果。有沒有一種方式來獲得類型名稱的格式與我指定的方式更接近? 例如:typeof(List<string>).OriginalName == "List<string>"C#:「漂亮」類型名稱函數?

+0

這很容易將自己寫成使用遞歸的擴展方法。 –

+2

這樣的函數不在框架中,因爲它取決於您使用的語言:VB具有不同的泛型表示,我也確定其他語言。 – svick

回答

0

我明白了,我必須自己寫這個。這是我的解決方案(它實際上比要求的要多一點)。它可能是有幫助的。

using System.Reflection; 
using HWClassLibrary.Debug; 
using System.Collections.Generic; 
using System.Linq; 
using System; 

namespace HWClassLibrary.Helper 
{ 
    public static class TypeNameExtender 
    { 
     private static IEnumerable<Type> _referencedTypesCache; 

     public static void OnModuleLoaded() { _referencedTypesCache = null; } 

     public static string PrettyName(this Type type) 
     { 
      if(type == typeof(int)) 
       return "int"; 
      if(type == typeof(string)) 
       return "string"; 

      var result = PrettyTypeName(type); 
      if(type.IsGenericType) 
       result = result + PrettyNameForGeneric(type.GetGenericArguments()); 
      return result; 
     } 

     private static string PrettyTypeName(Type type) 
     { 
      var result = type.Name; 
      if(type.IsGenericType) 
       result = result.Remove(result.IndexOf('`')); 

      if (type.IsNested && !type.IsGenericParameter) 
       return type.DeclaringType.PrettyName() + "." + result; 

      if(type.Namespace == null) 
       return result; 

      var conflictingTypes = ReferencedTypes 
       .Where(definedType => definedType.Name == type.Name && definedType.Namespace != type.Namespace) 
       .ToArray(); 

      var namespaceParts = type.Namespace.Split('.').Reverse().ToArray(); 
      var namespacePart = ""; 
      for(var i = 0; i < namespaceParts.Length && conflictingTypes.Length > 0; i++) 
      { 
       namespacePart = namespaceParts[i] + "." + namespacePart; 
       conflictingTypes = conflictingTypes 
        .Where(conflictingType => (conflictingType.Namespace + ".").EndsWith(namespacePart)) 
        .ToArray(); 
      } 

      return namespacePart + result; 
     } 

     private static IEnumerable<Type> ReferencedTypes 
     { 
      get 
      { 
       if(_referencedTypesCache == null) 
        _referencedTypesCache = Assembly.GetEntryAssembly().GetReferencedTypes(); 
       return _referencedTypesCache; 
      } 
     } 

     private static string PrettyNameForGeneric(Type[] types) 
     { 
      var result = ""; 
      var delim = "<"; 
      foreach(var t in types) 
      { 
       result += delim; 
       delim = ","; 
       result += t.PrettyName(); 
      } 
      return result + ">"; 
     } 
    } 
} 
2

你必須自己寫這個。請記住,Type.Name等正在調用生活在CLR中的方法,並且可以從多種語言中調用。這就是爲什麼它們不像C#或VB或調用者編碼的語言那樣回來,而是看起來像CLR表示。

還要注意,string以及什麼不是別名,如CLS類型,如System.String。同樣,這在你看到的格式中扮演着一個角色。

使用反射並不困難,但我質疑它的價值。

+0

我想知道爲什麼這是downvoted。 – jason

0

在你的榜樣,你可以期望的類型,所以你可以嘗試

public class test<T> where T : class 
{ 
    public List<String> tt 
    { 
     get; 
     set; 
    } 
} 
/////////////////////////// 
test<List<String>> tt = new test<List<String>>(); 
if(tt.GetType().FullName.Contains(TypeOf(List<String>).FullName)) 
{ 
    //do something 
} 
else 
{ 
    //do something else 
} 
28

用「漂亮」的名稱問題是他們根據你所使用的語言是不同的。想象一下,如果OriginalName返回C#語法,VB.NET開發人員會感到驚訝。

然而,這是很很容易這樣做你自己:

private static string PrettyName(Type type) 
{ 
    if (type.GetGenericArguments().Length == 0) 
    { 
     return type.Name; 
    } 
    var genericArguments = type.GetGenericArguments(); 
    var typeDefeninition = type.Name; 
    var unmangledName = typeDefeninition.Substring(0, typeDefeninition.IndexOf("`")); 
    return unmangledName + "<" + String.Join(",", genericArguments.Select(PrettyName)) + ">"; 
} 

這將遞歸解析非託管的名稱,因此,如果您有類似Dictionary<string, IList<string>>它應該仍然工作。

+4

理想情況下,這需要進一步的開發來處理'Dictionary > .KeyCollection'類型。它的CLR類型名稱是'Dictionary \'2 + KeyCollection [System.String,IList \'1 [System.String]]'(給出或使用命名空間限定符)。此外,你可以使用'Outer \'1 + Inner \'1 [OuterArg,InnerArg]'。當然你可以有數組(1D 2D等)和指向泛型的指針,需要處理。 :) – entheh

14

我以前CodeDomProvider轉換爲C#:

public static string GetOriginalName(this Type type) 
    { 
     string TypeName = type.FullName.Replace(type.Namespace + ".", "");//Removing the namespace 

     var provider = System.CodeDom.Compiler.CodeDomProvider.CreateProvider("CSharp"); //You can also use "VisualBasic" 
     var reference = new System.CodeDom.CodeTypeReference(TypeName); 

     return provider.GetTypeOutput(reference); 
    } 
+0

如果我想省略命名空間,該怎麼辦? – svick

+0

您可以使用type.Name而不是type.FullName我想這樣您就不必自己刪除名稱空間。對不起,我現在看到你沒有那樣獲取泛型參數。 – riezebosch

0

我知道你要比較的類型。
要做到這一點,最好的辦法...
myVar is List<string>
myVar.GetType() == myOtherVar.GetType()

如果您不需要這些......請忽略我的答案。

2

像哈羅德·霍耶的答案,但包括nullables和幾個內置類型:

/// <summary> 
/// Get full type name with full namespace names 
/// </summary> 
/// <param name="type"> 
/// The type to get the C# name for (may be a generic type or a nullable type). 
/// </param> 
/// <returns> 
/// Full type name, fully qualified namespaces 
/// </returns> 
public static string CSharpName(this Type type) 
{ 
    Type nullableType = Nullable.GetUnderlyingType(type); 
    string nullableText; 
    if (nullableType != null) 
    { 
     type = nullableType; 
     nullableText = "?"; 
    } 
    else 
    { 
     nullableText = string.Empty; 
    } 

    if (type.IsGenericType) 
    { 
     return string.Format(
      "{0}<{1}>{2}", 
      type.Name.Substring(0, type.Name.IndexOf('`')), 
      string.Join(", ", type.GetGenericArguments().Select(ga => ga.CSharpName())), 
      nullableText); 
    } 

    switch (type.Name) 
    { 
     case "String": 
      return "string"; 
     case "Int32": 
      return "int" + nullableText; 
     case "Decimal": 
      return "decimal" + nullableText; 
     case "Object": 
      return "object" + nullableText; 
     case "Void": 
      return "void" + nullableText; 
     default: 
      return (string.IsNullOrWhiteSpace(type.FullName) ? type.Name : type.FullName) + nullableText; 
    } 
} 
0

第一:榮譽對納維德車輪再造迴避。如果可以的話,我會加倍努力。

這裏有幾點需要補充,如果有人走這條路(至少對於VS10/.Net 4):
*嘗試使用TypeType參數的CodeTypeReference變體之一。這樣可以避免使用字符串類型名稱(如尾隨&)的一些缺陷,並且意味着您可以取回bool而不是System.Boolean等等。您可以獲取像這樣的很多類型的完整名稱空間,但是您總是可以擺脫稍後名稱空間部分。
*簡單Nullables往往回來的形式System.Nullable<int>而非int? - 您可以轉換成更好的語法與答案正則表達式,如: const string NullablePattern = @"System.Nullable<(?<nulledType>[\w\.]+)>"; const string NullableReplacement = @"${nulledType}?"; answer = Regex.Replace(answer, NullablePattern, NullableReplacement);
*方法的參數像out T?的類型給出了一個非常不透明的字符串。如果有人有處理這種事情的優雅方式,我很想知道這件事。

希望這一切都變得非常容易與羅斯林。

+0

對不起,Nullable模式是混亂的。應該是@"System\.Nullable<(?[\w\.]+)>";

2

這是我的實現。它是爲描述方法而創建的,因此它處理refout關鍵字。

private static Dictionary<Type, string> shorthandMap = new Dictionary<Type, string> 
{ 
    { typeof(Boolean), "bool" }, 
    { typeof(Byte), "byte" }, 
    { typeof(Char), "char" }, 
    { typeof(Decimal), "decimal" }, 
    { typeof(Double), "double" }, 
    { typeof(Single), "float" }, 
    { typeof(Int32), "int" }, 
    { typeof(Int64), "long" }, 
    { typeof(SByte), "sbyte" }, 
    { typeof(Int16), "short" }, 
    { typeof(String), "string" }, 
    { typeof(UInt32), "uint" }, 
    { typeof(UInt64), "ulong" }, 
    { typeof(UInt16), "ushort" }, 
}; 

private static string CSharpTypeName(Type type, bool isOut = false) 
{ 
    if (type.IsByRef) 
    { 
     return String.Format("{0} {1}", isOut ? "out" : "ref", CSharpTypeName(type.GetElementType())); 
    } 
    if (type.IsGenericType) 
    { 
     if (type.GetGenericTypeDefinition() == typeof(Nullable<>)) 
     { 
      return String.Format("{0}?", CSharpTypeName(Nullable.GetUnderlyingType(type))); 
     } 
     else 
     { 
      return String.Format("{0}<{1}>", type.Name.Split('`')[0], 
       String.Join(", ", type.GenericTypeArguments.Select(a => CSharpTypeName(a)).ToArray())); 
     } 
    } 
    if (type.IsArray) 
    { 
     return String.Format("{0}[]", CSharpTypeName(type.GetElementType())); 
    } 

    return shorthandMap.ContainsKey(type) ? shorthandMap[type] : type.Name; 
} 

調用代碼看起來是這樣的:

string line = String.Format("{0}.{1}({2})", 
    method.DeclaringType.Name, 
    method.Name, 
    String.Join(", ", method.GetParameters().Select(p => CSharpTypeName(p.ParameterType, p.IsOut) + " " + p.Name).ToArray())); 

methodMethodInfo實例。

注意:我沒有必要描述任何多維數組類型,所以我沒有打算實現這個描述,但通過調用type.GetArrayRank()來添加它會相當容易。

0

利用CodeDomProvider的最小工作解決方案是首先控制CodeTypeReference實例的構建方式。有特殊情況僅適用於泛型類型和多等級的陣列,所以我們只需要關心那些:

static CodeTypeReference CreateTypeReference(Type type) 
{ 
    var typeName = (type.IsPrimitive || type == typeof(string)) ? type.FullName : type.Name; 
    var reference = new CodeTypeReference(typeName); 
    if (type.IsArray) 
    { 
     reference.ArrayElementType = CreateTypeReference(type.GetElementType()); 
     reference.ArrayRank = type.GetArrayRank(); 
    } 

    if (type.IsGenericType) 
    { 
     foreach (var argument in type.GetGenericArguments()) 
     { 
      reference.TypeArguments.Add(CreateTypeReference(argument)); 
     } 
    } 
    return reference; 
} 

使用該改造的工廠方法,則有可能使用適當的代碼提供者得到相當打字,如下所示:

using (var provider = new CSharpCodeProvider()) 
{ 
    var reference = CreateTypeReference(typeof(IObservable<IEnumerable<Tuple<int?, string>>>[,])); 
    var output = provider.GetTypeOutput(reference); 
    Console.WriteLine(output); 
} 

上述代碼返回IObservable<IEnumerable<Tuple<Nullable<int>, string>>>[,]。唯一沒有得到妥善處理的特殊情況是Nullable類型,但這實際上比CodeDomProvider更重要。