2008-12-30 6 views
25

Je travaille sur la génération de code et j'ai rencontré un problème avec les génériques. Voici une version "simplifiée" de ce qui me cause des problèmes.Comment puis-je obtenir la bonne définition de texte d'un type générique en utilisant la réflexion?

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

Avec le code ci-dessus snippet la valeur de text est la suivante: (. Les sauts de ligne ajouté pour une meilleure lisibilité)

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

Y at-il un moyen d'obtenir le nom du type (type) dans un format différent sans analyser la chaîne ci-dessus? Je désire le résultat suivant pour text:

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

Notez que si vous supprimez '' .FullName' et utilisez .ToString() 'la place, vous obtenez le" text "' 'System.Collections.Generic.Dictionary'2 [System.String, System.DateTime]' 'qui est plus lisible, et proche de ce que vous voulez. –

Répondre

27

Il n'y a pas moyen intégré pour obtenir cette représentation dans le Framework .Net. A savoir parce qu'il n'y a aucun moyen de le corriger. Il y a un bon nombre de constructions qui ne sont pas représentables dans la syntaxe de style C#. Par exemple "<> foo" est un nom de type valide dans IL mais ne peut pas être représenté en C#. Cependant, si vous cherchez une solution assez bonne, elle peut être implémentée manuellement assez rapidement. La solution ci-dessous fonctionnera pour la plupart des situations. Il ne traitera pas

  1. Types emboîtées
  2. noms illégaux C#
  3. Couple d'autres scénarios

Exemple:

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

Au-dessus et au-delà! Cela fonctionne parfaitement! Je vous remercie!! –

+0

si vous avez une chance modifier votre réponse et inclure "chaîne statique GetFriendlyTypeName (type Type) {if (type.IsGenericParameter) {return type.Name;}" dans le bloc de code :) –

+0

@Jamey, done. C'est en fait assez bizarre. Le bloc de code n'ajouterait pas la première ligne jusqu'à ce que j'ajoute une ligne sans code entre la liste ordonnée et le début du bloc. – JaredPar

0

Je ne serait intégré pense pas .NET a rien que cela, vous devrez le faire vous-même. Je pense que les classes de réflexion fournissent assez d'informations pour reconstruire le nom du type dans ce formulaire.

+0

J'ai essayé ce chemin pouvez-vous élaborer sur la solution? Je n'ai pas trouvé de propriétés ou de collections qui me donnent cette information que je peux utiliser pour reconstruire la déclaration. –

0

Je crois que vous pouvez passer

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

en Type.Parse(). C'est un nom de type complet, je pense.

+0

Pas nécessaire. L'original Dictionary.GetType() contient déjà une collection de paramètres de type auxquels vous pouvez accéder directement. Pas besoin d'analyser pour obtenir le même résultat. –

2
string text = dictionary.ToString(); 

fournit presque ce que vous demandez:

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

Ce n'est pas une solution générique cependant. Tout type qui utilise .ToString() va rompre cette solution – JaredPar

+0

@JaredPar Ceci est résolu en utilisant 'dictionary.GetType(). ToString()' à la place. –

4

Ce soir, je jouais un peu avec les méthodes d'extension et j'ai essayé de trouver une réponse à votre question. Voici le résultat: c'est un code sans 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

Merci! Cette solution fonctionne aussi. –

19

Une bonne alternative et propre, grâce à @LukeH's comment, est

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

beaucoup mieux puis marqué comme réponse (sous-classes de classes génériques sont affichées correctement) – FLCL

+2

Des implications de performance de cela? –

Questions connexes