Подтвердить что ты не робот

Как я могу получить правильное текстовое определение общего типа с использованием отражения?

Я работаю над созданием кода и попал в зацепку с дженериками. Вот "упрощенная" версия того, что вызывает у меня проблемы.

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>
4b9b3361

Ответ 1

Нет встроенного способа получить это представление в .NET Framework. А именно потому, что нет способа получить это правильно. Существует большое количество конструктов, которые не представлены в синтаксисе стиля С#. Например, "< > foo" является допустимым именем типа в IL, но не может быть представлено на С#.

Однако, если вы ищете довольно хорошее решение, его можно выполнить довольно быстро. Решение ниже будет работать для большинства ситуаций. Он не будет обрабатывать

  • Вложенные типы
  • Нелегальные имена С#
  • Пара других сценариев

Пример:

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

Ответ 2

Хорошая и чистая альтернатива, благодаря комментарий @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);
    }
}

Ответ 3

В этот вечер я немного тренировался с методами расширения, и я попытался найти ответ на ваш вопрос. Вот результат: это не-гарантийный код.; -)

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

Ответ 4

string text = dictionary.ToString();

предоставляет почти то, о чем вы просите:

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

Ответ 5

Я не думаю, что у .NET есть что-то встроенное, чтобы это сделать, поэтому вам придется делать это самостоятельно. Я думаю, что классы отражения предоставляют достаточно информации для восстановления имени типа в этой форме.

Ответ 6

Я считаю, что вы можете пройти

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

в Type.Parse(). Я думаю, это вполне квалифицированное имя типа.