Я работаю над генерацией кода и столкнулся с проблемой с дженериками. Вот «упрощенная» версия того, что вызывает у меня проблемы.
Dictionary<string, DateTime> dictionary = new Dictionary<string, DateTime>();
string text = dictionary.GetType().FullName;
В приведенном выше фрагменте кода значение text выглядит следующим образом:
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]]
(Для удобства чтения добавлены разрывы строк.)
Есть ли способ получить имя типа (type) в другом формате без анализа указанной выше строки? Мне нужен следующий результат для text:
System.Collections.Generic.Dictionary<System.String, System.DateTime>





Я не думаю, что в .NET есть что-то встроенное, что могло бы сделать это, поэтому вам придется делать это самостоятельно. Я думаю, что классы отражения предоставляют достаточно информации, чтобы восстановить имя типа в этой форме.
Я пробовал этот путь, вы можете подробнее рассказать о решении? Я не нашел свойств или коллекций, которые дают мне эту информацию, которую я могу использовать для восстановления объявления.
Я верю, ты можешь пройти
System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
в Type.Parse(). Я думаю, это полное имя типа.
Не требуется. Исходный Dictionary.GetType () уже содержит набор параметров типа, к которым вы можете получить доступ напрямую. Для получения того же результата не требуется синтаксический анализ.
string text = dictionary.ToString();
предоставляет почти то, о чем вы просите:
System.Collections.Generic.Dictionary`2[System.String,System.DateTime]
Однако это не универсальное решение. Любой тип, который использует .ToString (), нарушит это решение
@JaredPar Это решается с помощью dictionary.GetType().ToString().
Нет встроенного способа получить это представление в .Net Framework. А именно потому, что нет возможности исправить это. Существует большое количество конструкций, которые не могут быть представлены в синтаксисе стиля C#. Например, «<> foo» - допустимое имя типа в IL, но не может быть представлено в C#.
Однако, если вы ищете довольно хорошее решение, его можно довольно быстро реализовать вручную. Приведенное ниже решение будет работать в большинстве ситуаций. Это не справится
Пример:
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();
}
если у вас есть возможность, отредактируйте свой ответ и включите "статическую строку GetFriendlyTypeName (Type type) {if (type.IsGenericParameter) {return type.Name;}" в блок кода :)
@ Джейми, готово. На самом деле это довольно странно. Блок кода не будет добавлять первую строку, пока я не добавлю строку без кода между упорядоченным списком и началом блока.
и добавьте сверху: bool bArray = false; если (type.IsArray) {bArray = true; type = type.GetElementType (); } и внизу if (bArray) builder.Append ("[]"); return builder.ToString (); для поддержки массивов.
Вы можете обрабатывать значения Nullables и добавить пробел после запятой.
первый блок «if (type.IsGenericParameter) {...}» не может быть хорошей идеей, поскольку параметр универсального типа может быть самим универсальным типом.
Такие вещи, как GetFriendlyTypeName(new List<int>().GetEnumerator().GetType()) или GetFriendlyTypeName(typeof(List<>.Enumerator)), приводят к исключению. Это типы, вложенные в универсальные внешние типы.
В этот вечер я немного поигрался с методами расширения и попытался найти ответ на ваш вопрос. Вот результат: это код без гарантии. ;-)
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);
}
}
Хорошая и чистая альтернатива, благодаря Комментарий @ LukeH, -
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);
}
}
намного лучше, чем помечено как ответ (подклассы общих классов показаны правильно)
Обратите внимание, что если вы удалите
.FullNameи вместо этого используете.ToString(), вы получите «текстовый»System.Collections.Generic.Dictionary`2[System.String,System.DateTime], который более читабелен и близок к тому, что вы хотите.