【问题标题】:How can I get the primitive name of a type in C#?如何在 C# 中获取类型的原始名称?
【发布时间】:2021-06-11 12:24:33
【问题描述】:

我正在使用反射打印出方法签名,例如

foreach (var pi in mi.GetParameters()) {
    Console.WriteLine(pi.Name + ": " + pi.ParameterType.ToString());
}

这很好用,但它会将基元的类型打印为“System.String”而不是“string”和“System.Nullable`1[System.Int32]”而不是“int?”。有没有办法获取代码中参数的名称,例如

public Example(string p1, int? p2)

打印

p1: string
p2: int?

而不是

p1: System.String
p2: System.Nullable`1[System.Int32]

【问题讨论】:

标签: c# reflection


【解决方案1】:

编辑:我在下面的答案中错了一半。

看看CSharpCodeProvider.GetTypeOutput。示例代码:

using Microsoft.CSharp;
using System;
using System.CodeDom;

class Test
{
    static void Main()
    {
        var compiler = new CSharpCodeProvider();
        // Just to prove a point...
        var type = new CodeTypeReference(typeof(Int32));
        Console.WriteLine(compiler.GetTypeOutput(type)); // Prints int
    }
}

但是,这个不会Nullable<T> 翻译成T? - 我找不到任何可以做到这一点的选项,尽管这并不意味着这样的选项不会'不存在:)


框架中没有任何东西支持这一点 - 毕竟,它们是 C# 特定的名称。

(顺便说一下,string不是primitive type。)

您必须自己发现Nullable`1(例如,Nullable.GetUnderlyingType 可能用于此),并拥有从完整框架名称到每个别名的映射。

【讨论】:

  • CSharpCodeProvider.GetTypeOutput 不会将 System.String 更改为 string
  • 现在也可以...
  • @DennisvanGils:我相信这只是继承自Component。我怀疑在大多数情况下,不丢弃它是可以的 - 但这样做不会有坏处。
  • @Mic:如果您希望它与 in/out/ref 参数一起使用,请先通过 if (type.HasElementType) type = type.GetElementType(); 运行它(不过,您可能希望在此处对数组类型进行特殊处理)。如果您还要解决可空值,请在 之前 Nullable.GetUnderlyingType.
【解决方案2】:

这个question 有两个有趣的答案。来自 Jon Skeet accepted one 几乎说出了他已经说过的话。

编辑 乔恩更新了他的答案,所以它和我现在的答案几乎一样。 (但当然要早 20 秒)

但 Luke H 也提供了 this answer,我认为这是对 CodeDOM 的非常棒的使用。

Type t = column.DataType;    // Int64

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

    var prov = new CSharpCodeProvider();
    prov.GenerateCodeFromExpression(expr, sw, new CodeGeneratorOptions());
}

Console.WriteLine(sb.ToString());    // long

【讨论】:

    【解决方案3】:

    不是世界上最漂亮的代码,但这是我最终做的: (基于康纳德的代码)

    public static string CSharpName(this Type type)
    {
        if (!type.FullName.StartsWith("System"))
            return type.Name;
        var compiler = new CSharpCodeProvider();
        var t = new CodeTypeReference(type);
        var output = compiler.GetTypeOutput(t);
        output = output.Replace("System.","");
        if (output.Contains("Nullable<"))
            output = output.Replace("Nullable","").Replace(">","").Replace("<","") + "?";
        return output;
    }
    

    【讨论】:

    • 警告!不适用于嵌套泛型,因为 &lt;&gt; 被盲目替换
    【解决方案4】:

    另一个选项,基于此处的其他答案。

    特点:

    • String 转换为stringInt32 转换为int
    • Nullable&lt;Int32&gt; 处理为int?
    • System.DateTime 抑制为DateTime
    • 所有其他类型都写完整

    它处理我需要的简单情况,不确定它是否能很好地处理复杂类型..

    Type type = /* Get a type reference somehow */
    var compiler = new CSharpCodeProvider();
    if (type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
    {
        return compiler.GetTypeOutput(new CodeTypeReference(type.GetGenericArguments()[0])).Replace("System.","") + "?";
    }
    else
    {
        return compiler.GetTypeOutput(new CodeTypeReference(type)).Replace("System.","");
    }   
    

    【讨论】:

    • 已经很久了,但我相信它是:var compiler = new CSharpCodeProvider();
    • 获取可空类型的方法要简单得多:pastebin.com/evjr4Umh
    【解决方案5】:

    没有。 string 只是 System.String 的代表——string 在幕后并没有真正的意义。

    顺便说一句,要通过System.Nullable'1[System.Int32],您可以使用Nullable.GetUnderlyingType(type);

    【讨论】:

      【解决方案6】:

      这是我在大约 5 分钟的黑客攻击后得出的结论。例如:

      CSharpAmbiance.GetTypeName(typeof(IDictionary<string,int?>))
      

      将返回System.Collections.Generic.IDictionary&lt;string, int?&gt;

      public static class CSharpAmbiance
      {
          private static Dictionary<Type, string> aliases =
              new Dictionary<Type, string>();
      
          static CSharpAmbiance()
          {
              aliases[typeof(byte)] = "byte";
              aliases[typeof(sbyte)] = "sbyte";
              aliases[typeof(short)] = "short";
              aliases[typeof(ushort)] = "ushort";
              aliases[typeof(int)] = "int";
              aliases[typeof(uint)] = "uint";
              aliases[typeof(long)] = "long";
              aliases[typeof(ulong)] = "ulong";
              aliases[typeof(char)] = "char";
      
              aliases[typeof(float)] = "float";
              aliases[typeof(double)] = "double";
      
              aliases[typeof(decimal)] = "decimal";
      
              aliases[typeof(bool)] = "bool";
      
              aliases[typeof(object)] = "object";
              aliases[typeof(string)] = "string";
          }
      
          private static string RemoveGenericNamePart(string name)
          {
              int backtick = name.IndexOf('`');
      
              if (backtick != -1)
                  name = name.Substring(0, backtick);
      
              return name;
          }
      
          public static string GetTypeName(Type type)
          {
              if (type == null)
                  throw new ArgumentNullException("type");
      
              string keyword;
              if (aliases.TryGetValue(type, out keyword))
                  return keyword;
      
              if (type.IsArray) {
                  var sb = new StringBuilder();
      
                  var ranks = new Queue<int>();
                  do {
                      ranks.Enqueue(type.GetArrayRank() - 1);
                      type = type.GetElementType();
                  } while (type.IsArray);
      
                  sb.Append(GetTypeName(type));
      
                  while (ranks.Count != 0) {
                      sb.Append('[');
      
                      int rank = ranks.Dequeue();
                      for (int i = 0; i < rank; i++)
                          sb.Append(',');
      
                      sb.Append(']');
                  }
      
                  return sb.ToString();
              }
      
              if (type.IsGenericTypeDefinition) {
                  var sb = new StringBuilder();
      
                  sb.Append(RemoveGenericNamePart(type.FullName));
                  sb.Append('<');
      
                  var args = type.GetGenericArguments().Length - 1;
                  for (int i = 0; i < args; i++)
                      sb.Append(',');
      
                  sb.Append('>');
      
                  return sb.ToString();
              }
      
              if (type.IsGenericType) {
                  if (type.GetGenericTypeDefinition() == typeof(Nullable<>))
                      return GetTypeName(type.GetGenericArguments()[0]) + "?";
      
                  var sb = new StringBuilder();
      
                  sb.Append(RemoveGenericNamePart(type.FullName));
                  sb.Append('<');
      
                  var args = type.GetGenericArguments();
                  for (int i = 0; i < args.Length; i++) {
                      if (i != 0)
                          sb.Append(", ");
      
                      sb.Append(GetTypeName(args[i]));
                  }
      
                  sb.Append('>');
      
                  return sb.ToString();
              }
      
              return type.FullName;
          }
      }
      

      【讨论】:

      • 请参阅我关于该主题的文章,了解为什么会这样:blogs.msdn.com/b/ericlippert/archive/2009/08/17/…
      • 两者。如果您要为问题提供解决方案,那么实际解决所述问题是值得的,并且在许多情况下提供错误解决所述问题的半生不熟的解决方案是有害的。更大的一点是你的设计方法还有很多不足之处。与其编写一个糟糕的解决方案,然后在每次有人发现错误时添加补丁,不如编写一个考虑所有情况的规范首先,然后对规范进行编码。
      • 因为辩证法是一种高效的教学技术。如果您或其他人从中学到了一些有价值的东西,我认为重新编码并不是毫无意义的。我的解决方案没有出现;一个正确的解决方案至少需要我半天的时间来编写和测试,而我没有那种时间。
      • @cdhowie:请记住,他正在尝试教其他任何人在未来阅读这篇文章,所以可能不是一个简单的个人侮辱尝试。
      • @asawyer:这不是任何形式的侮辱,个人或其他方面的尝试。对于一个合理的技术问题的严重缺陷答案,这是非常有建设性和善意的反馈。这是一个协作编辑的网站,旨在为技术问题提供准确的答案;建设性反馈是设计的一部分。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-26
      • 1970-01-01
      • 1970-01-01
      • 2015-04-01
      • 2010-11-06
      相关资源
      最近更新 更多