2008-12-30 4 views
25

Ich arbeite an der Code-Generierung und geriet in einen Konflikt mit Generika. Hier ist eine "vereinfachte" Version dessen, was mich Probleme verursacht.Wie kann ich die korrekte Textdefinition eines generischen Typs mit Reflektion erhalten?

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

Mit dem obigen Codeausschnitt des Wert text wie folgt: (. Zeilenumbrüche zur besseren Lesbarkeit hinzugefügt)

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]] 

Gibt es eine Möglichkeit die Typnamen zu erhalten (type) in einem anderen Format, ohne die obige Zeichenfolge zu analysieren? Ich wünsche das folgende Ergebnis für text:

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

Beachten Sie, dass, wenn Sie '.FullName' entfernen und verwenden' .ToString() 'statt, erhalten Sie das" text "' 'System.Collections.Generic.Dictionary'2 [System.String, System.DateTime]' 'das ist besser lesbar und nah an dem, was Sie wollen. –

Antwort

27

Es gibt keine integrierte Möglichkeit, diese Darstellung in .NET Framework zu erhalten. Nämlich weil es keinen Weg gibt, es richtig zu machen. Es gibt eine gute Anzahl von Konstrukten, die in der C# -Stilsyntax nicht darstellbar sind. Zum Beispiel "<> foo" ist ein gültiger Typname in IL, kann aber nicht in C# dargestellt werden.

Wenn Sie jedoch nach einer ziemlich guten Lösung suchen, kann diese ziemlich schnell von Hand implementiert werden. Die folgende Lösung funktioniert für die meisten Situationen. Es wird nicht

  1. verschachtelte Typen
  2. Illegal C# Namen
  3. Paar von anderen Szenarien

Beispiel handhaben:

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

Darüber hinaus! Das funktioniert perfekt! Danke!! –

+0

Wenn Sie eine Chance bekommen, bearbeiten Sie Ihre Antwort und fügen Sie "static string GetFriendlyTypeName (Typ typ) {if (type.IsGenericParameter) {return type.Name;}" in den Codeblock ein :) –

+0

@Jamey, done. Das ist eigentlich ziemlich seltsam. Der Code-Block würde die erste Zeile nicht hinzufügen, bis ich eine Nicht-Code-Zeile zwischen der geordneten Liste und dem Anfang des Blocks hinzugefügt habe. – JaredPar

0

Ich glaube nicht, .NET alles hat eingebaute, dass dies tun würde, so werden Sie es selbst zu tun haben. Ich denke, dass die Reflexionsklassen genügend Informationen liefern, um den Typnamen in dieser Form zu rekonstruieren.

+0

Ich habe diesen Weg versucht, können Sie die Lösung näher ausführen? Ich habe keine Eigenschaften oder Sammlungen gefunden, die mir diese Informationen liefern, die ich zur Rekonstruktion der Deklaration verwenden kann. –

0

Ich glaube, Sie

System.String passieren kann, mscorlib, Version = 2.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089

in Type.Parse(). Das ist ein voll qualifizierter Typenname, denke ich.

+0

Nicht benötigt. Das ursprüngliche Dictionary.GetType() enthält bereits eine Sammlung von Typparametern, auf die Sie direkt zugreifen können. Kein Parsen nötig, um das gleiche Ergebnis zu erhalten. –

2
string text = dictionary.ToString(); 

bietet fast, was Sie fordern:

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

Dies ist jedoch keine generische Lösung. Jeder Typ, der .ToString() verwendet, bricht diese Lösung – JaredPar

+0

@JaredPar Das wird gelöst, indem 'dictionary.GetType(). ToString()' stattdessen verwendet. –

4

An diesem Abend war ich ein wenig mit Erweiterungsmethoden liebäugelt und ich habe versucht, eine Antwort auf deine Frage zu finden. Hier ist das Ergebnis: Es ist ein Code ohne Garantie. ;-)

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); 
    } 
} 
+0

Danke! Diese Lösung funktioniert auch. –

19

Eine gute und saubere Alternative, dank @LukeH's comment ist

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); 
    } 
} 
+3

viel besser als als Antwort markiert (Unterklassen von generischen Klassen werden korrekt angezeigt) – FLCL

+2

Irgendwelche Auswirkungen auf die Leistung von diesem? –