【问题标题】:How can I get the correct text definition of a generic type using reflection?如何使用反射获得泛型类型的正确文本定义?
【发布时间】:2008-12-30 22:13:42
【问题描述】:

我正在处理代码生成,但遇到了泛型问题。这是导致我出现问题的“简化”版本。

Dictionary<string, DateTime> dictionary = new Dictionary<string, DateTime>();
string text = dictionary.GetType().FullName;

使用上面的代码sn -p,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],并且更接近您想要的。

标签: c# generics reflection


【解决方案1】:

在 .Net 框架中没有内置的方法来获得这种表示。即因为没有办法让它正确。有很多构造不能用 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,完成。这实际上很奇怪。代码块不会添加第一行,直到我在有序列表和块的开头之间添加了一个非代码行。
  • 并在顶部添加:bool bArray = false; if (type.IsArray) { bArray = true; type = type.GetElementType(); } 和底部 if (bArray) builder.Append("[]");返回 builder.ToString();支持数组。
  • 您可能想要处理 Nullables,并且您可能想要在逗号后添加一个空格。
  • 第一个块“ if (type.IsGenericParameter) {...} ”可能不是一个好主意,因为泛型类型参数本身可能是泛型类型。
【解决方案2】:

感谢@LukeH's comment,一个不错的替代方案是

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]
    

    【讨论】:

    • 这不是一个通用的解决方案。任何使用 .ToString() 的类型都会破坏这个解决方案
    • @JaredPar 这可以通过使用dictionary.GetType().ToString() 来解决。
    【解决方案5】:

    我不认为 .NET 有任何内置功能可以做到这一点,所以你必须自己做。我认为反射类提供了足够的信息来重构这种形式的类型名称。

    【讨论】:

    • 这条路径我试过了,你能详细说明一下解决方案吗?我没有找到可以为我提供可用于重建声明的信息的属性或集合。
    【解决方案6】:

    相信你可以通过

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

    进入Type.Parse()。我认为这是一个完全限定的类型名称。

    【讨论】:

    • 不需要。原来的 Dictionary.GetType() 已经包含了可以直接访问的类型参数的集合。无需解析即可获得相同的结果。
    猜你喜欢
    • 2021-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-12
    相关资源
    最近更新 更多