¿Cómo puedo obtener la definición de texto correcta de un tipo genérico utilizando la reflexión?

Estoy trabajando en la generación de código y me encontré con un problema con los generics. Aquí hay una versión “simplificada” de lo que me está causando problemas.

Dictionary dictionary = new Dictionary(); string text = dictionary.GetType().FullName; 

Con el fragmento de código anterior, el valor del text es el siguiente:

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

(Se agregaron saltos de línea para una mejor legibilidad).

¿Hay alguna manera de obtener el tipo de nombre ( type ) en un formato diferente sin analizar la cadena anterior? Deseo el siguiente resultado para el text :

 System.Collections.Generic.Dictionary 

No hay una forma incorporada de obtener esta representación en .Net Framework. A saber, porque no hay forma de hacerlo correcto. Hay un buen número de construcciones que no son representables en la syntax de estilo C #. Por ejemplo, “<> foo” es un nombre de tipo válido en IL pero no se puede representar en C #.

Sin embargo, si está buscando una solución bastante buena, puede implementarla a mano con bastante rapidez. La siguiente solución funcionará para la mayoría de las situaciones. No manejará

  1. Tipos nesteds
  2. Nombres ilegales de C #
  3. Pareja de otros escenarios

Ejemplo:

 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(); } 

Una buena y limpia alternativa, gracias al comentario de @ LukeH , es

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

Esta noche estaba jugando un poco con los métodos de extensión e intenté encontrar una respuesta para su pregunta. Este es el resultado: es un código sin garantía. 😉

 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(), new List(), new List>(), 0 }; Type t = testCases[0].GetType(); string text = t.GetCodeDefinition(); Console.WriteLine(text); } } 
 string text = dictionary.ToString(); 

proporciona casi lo que estás pidiendo:

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

No creo que .NET tenga nada incorporado que pueda hacer esto, así que tendrás que hacerlo tú mismo. Creo que las clases de reflexión proporcionan información suficiente para reconstruir el nombre del tipo en esta forma.

Creo que puedes pasar

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

en Type.Parse() . Ese es un nombre de tipo completamente calificado, creo.