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

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

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>

Обратите внимание, что если вы удалите .FullName и вместо этого используете .ToString(), вы получите «текстовый» System.Collections.Generic.Dictionary`2[System.String,System‌​.DateTime], который более читабелен и близок к тому, что вы хотите.

Jeppe Stig Nielsen 01.10.2013 16:39
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
26
1
5 094
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

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

Я пробовал этот путь, вы можете подробнее рассказать о решении? Я не нашел свойств или коллекций, которые дают мне эту информацию, которую я могу использовать для восстановления объявления.

Jamey McElveen 31.12.2008 01:38

Я верю, ты можешь пройти

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

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

Не требуется. Исходный Dictionary.GetType () уже содержит набор параметров типа, к которым вы можете получить доступ напрямую. Для получения того же результата не требуется синтаксический анализ.

Vilx- 31.12.2008 01:28
string text = dictionary.ToString();

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

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

Однако это не универсальное решение. Любой тип, который использует .ToString (), нарушит это решение

JaredPar 31.12.2008 03:32

@JaredPar Это решается с помощью dictionary.GetType().ToString().

Jeppe Stig Nielsen 01.10.2013 16:40
Ответ принят как подходящий

Нет встроенного способа получить это представление в .Net Framework. А именно потому, что нет возможности исправить это. Существует большое количество конструкций, которые не могут быть представлены в синтаксисе стиля C#. Например, «<> foo» - допустимое имя типа в IL, но не может быть представлено в C#.

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

  1. Вложенные типы
  2. Незаконные имена C#
  3. Пара других сценариев

Пример:

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;}" в блок кода :)

Jamey McElveen 31.12.2008 17:39

@ Джейми, готово. На самом деле это довольно странно. Блок кода не будет добавлять первую строку, пока я не добавлю строку без кода между упорядоченным списком и началом блока.

JaredPar 31.12.2008 17:49

и добавьте сверху: bool bArray = false; если (type.IsArray) {bArray = true; type = type.GetElementType (); } и внизу if (bArray) builder.Append ("[]"); return builder.ToString (); для поддержки массивов.

Wolf5 23.03.2009 16:05

Вы можете обрабатывать значения Nullables и добавить пробел после запятой.

SLaks 05.07.2011 18:38

первый блок «if (type.IsGenericParameter) {...}» не может быть хорошей идеей, поскольку параметр универсального типа может быть самим универсальным типом.

Johnny5 05.07.2011 18:58

Такие вещи, как GetFriendlyTypeName(new List<int>().GetEnumerator().GetType()) или GetFriendlyTypeName(typeof(List<>.Enumerator)), приводят к исключению. Это типы, вложенные в универсальные внешние типы.

Jeppe Stig Nielsen 01.10.2013 16:31

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

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

намного лучше, чем помечено как ответ (подклассы общих классов показаны правильно)

FLCL 03.02.2014 23:54

Другие вопросы по теме