Правильный способ получить базовое имя универсального типа в .NET через Substring? - PullRequest
8 голосов
/ 30 августа 2009

Если у меня есть это:

Type t = typeof(Dictionary<String, String>);

Как мне получить "System.Collections.Generic.Dictionary" в виде строки? Это лучший / единственный способ сделать это:

String n = t.FullName.Substring(0, t.FullName.IndexOf("`"));

Кажется, что-то вроде хакерства для меня.

Причина, по которой я этого хочу, заключается в том, что я хочу взять объект Type и создать код, аналогичный тому, который содержится в файле исходного кода C #. Я создаю несколько текстовых шаблонов, и мне нужно добавить типы в виде строк в источник, а свойство FullName создает что-то вроде этого:

System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Version=2.0.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089]]

вместо того, что я хочу:

System.Collections.Generic.Dictionary<System.String, System.String>

Редактировать : Хорошо, вот окончательный код, мне все еще кажется хаком, но он работает:

/// <summary>
/// This method takes a type and produces a proper full type name for it, expanding generics properly.
/// </summary>
/// <param name="type">
/// The type to produce the full type name for.
/// </param>
/// <returns>
/// The type name for <paramref name="type"/> as a string.
/// </returns>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="type"/> is <c>null</c>.</para>
/// </exception>
public static String TypeToString(Type type)
{
    #region Parameter Validation

    if (Object.ReferenceEquals(null, type))
        throw new ArgumentNullException("type");

    #endregion

    if (type.IsGenericType)
    {
        if (type.GetGenericTypeDefinition() == typeof(Nullable<>))
        {
            Type underlyingType = type.GetGenericArguments()[0];
            return String.Format("{0}?", TypeToString(underlyingType));
        }
        String baseName = type.FullName.Substring(0, type.FullName.IndexOf("`"));
        return baseName + "<" + String.Join(", ", (from paramType in type.GetGenericArguments()
                                                   select TypeToString(paramType)).ToArray()) + ">";
    }
    else
    {
        return type.FullName;
    }
}

Ответы [ 4 ]

7 голосов
/ 30 августа 2009

Проблема в том, что вам нужна нотация для конкретного языка, в данном случае C #.

IL syntax:     [mscorlib]System.Collections.Generic.Dictionary`2<class [mscorlib]System.String, class [mscorlib]System.String>
C# syntax:     System.Collections.Generic.Dictionary<System.String, System.String>
VB.NET syntax: System.Collections.Generic.Dictionary(Of System.String, system.String)

Возможно, вы можете вызвать языковую службу, чтобы получить желаемую строку, но вам, вероятно, лучше сгенерировать строку самостоятельно.

6 голосов
/ 30 августа 2009

Вы можете использовать CodeDom для генерации более "нормального" вида объявления в стиле C #.

CodeDomProvider csharpProvider = CodeDomProvider.CreateProvider("C#");
CodeTypeReference typeReference = new CodeTypeReference(typeof(Dictionary<string, int>));
CodeVariableDeclarationStatement variableDeclaration = new CodeVariableDeclarationStatement(typeReference, "dummy");
StringBuilder sb = new StringBuilder();
using (StringWriter writer = new StringWriter(sb))
{
    csharpProvider.GenerateCodeFromStatement(variableDeclaration, writer, new CodeGeneratorOptions());
}

sb.Replace(" dummy;", null);
Console.WriteLine(sb.ToString());

Приведенный выше код имеет следующий вывод:

System.Collections.Generic.Dictionary<string, int>    

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

3 голосов
/ 01 сентября 2009

Это ближе к тому, что вы ищете?

Он использует CodeTypeReferenceExpression и не требует дальнейших вызовов Substring или Replace:

var type = typeof(Dictionary<string, string>);
Console.WriteLine(TypeToString(type));

// ...

public static string TypeToString(Type type)
{
    if (type == null) throw new ArgumentNullException("type");

    var sb = new StringBuilder();
    using (var sw = new StringWriter(sb))
    {
        var expr = new CodeTypeReferenceExpression(type);

        var prov = new CSharpCodeProvider();
        prov.GenerateCodeFromExpression(expr, sw, new CodeGeneratorOptions());
    }
    return sb.ToString();
}
2 голосов
/ 30 августа 2009

AFAIK внутренняя нотация для универсальных типов использует нотацию `x для указания числа параметров типа для универсального типа. То есть 2 указывает, что словарь нуждается в двух типах, чтобы закрыть его. Это то же самое обозначение, которое используется ILDasm и Reflection.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...