2008-12-30 18 views
25

Pracuję nad generowaniem kodu i wpadłem w pułapkę z rodzajami. Oto "uproszczona" wersja tego, co powoduje problemy.Jak uzyskać poprawną definicję tekstu typu ogólnego za pomocą odbicia?

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

Z powyższego fragmentu kodu wartość text jest następujący: (. Podziały wierszy dodane dla lepszej czytelności)

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

Czy istnieje sposób, aby uzyskać nazwę typu (type) w innym formacie bez analizowania powyższego ciągu? Pragnę następujący wynik dla text:

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

Zauważ, że jeśli usunąć '.FullName' i używać' .ToString() 'Zamiast tego uzyskać" text "' 'System.Collections.Generic.Dictionary'2 [System.String, System.DateTime]' 'który jest bardziej czytelny i bliski temu, co chcesz. –

Odpowiedz

27

Nie ma wbudowaną sposób, aby uzyskać tę reprezentację w .NET Framework. Mianowicie, ponieważ nie ma sposobu, aby to naprawić. Istnieje duża liczba konstruktów, których nie można przedstawić w stylu C#. Na przykład "<> foo" to poprawna nazwa typu w IL, ale nie może być reprezentowana w C#.

Jednakże, jeśli szukasz całkiem niezłego rozwiązania, można je dość szybko wdrożyć ręcznie. Poniższe rozwiązanie będzie działać w większości sytuacji. To nie zajmie

  1. zagnieżdżone typy
  2. Nielegalne C# Nazwy
  3. kilka innych scenariuszy

Przykład:

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

Ponad wszystko! Działa to doskonale! Dziękuję Ci!! –

+0

jeśli masz szansę, edytuj swoją odpowiedź i dołącz "static string GetFriendlyTypeName (Type type) {if (type.IsGenericParameter) {return type.Name;}" w bloku kodu :) –

+0

@Jamey, gotowe. To jest naprawdę dość dziwne. Blok kodu nie doda pierwszej linii, dopóki nie dodaję linii nie kodującej między uporządkowaną listą a początkiem bloku. – JaredPar

0

Nie sądzę .NET ma coś wbudowaną że byłoby to zrobić, więc trzeba będzie to zrobić samemu. Myślę, że klasy refleksów dostarczają dość informacji, aby zrekonstruować nazwę typu w tej formie.

+0

Próbowałem już tej ścieżki, czy możesz opracować rozwiązanie? Nie znalazłem nieruchomości ani kolekcji, które dostarczyłyby mi tych informacji, których mogę użyć do rekonstrukcji deklaracji. –

0

wierzę można przekazać

System.String, mscorlib, Version = 2.0.0.0, Culture = neutral, TokenKluczaPublicznego = b77a5c561934e089

do Type.Parse(). To chyba w pełni kwalifikowana nazwa typu.

+0

Niepotrzebne. Oryginalna Dictionary.GetType() już zawiera kolekcję parametrów typów, do których można uzyskać bezpośredni dostęp. Nie ma potrzeby parsowania, aby uzyskać ten sam wynik. –

2
string text = dictionary.ToString(); 

zapewnia niemal co prosicie:

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

To jednak nie jest ogólne rozwiązanie. Dowolny typ, który używa .ToString(), rozwiąże to rozwiązanie. – JaredPar

+0

@JaredPar Rozwiązanie to polega na użyciu 'dictionary.GetType(). ToString()' zamiast tego. –

4

Ten wieczór bawił się trochę z metod rozszerzenie i próbowałem znaleźć odpowiedź na twoje pytanie. Oto wynik: to kod bez gwarancji. ;-)

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

Dzięki! Te rozwiązania też działają. –

19

Dobry i czysty alternatywa, dzięki @LukeH's comment jest

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

o wiele lepiej niż zaznaczono jako odpowiedź (podklasy klas ogólnych są wyświetlane poprawnie) – FLCL

+2

Jakiekolwiek konsekwencje tego działania? –

Powiązane problemy