【问题标题】:How does enum.toString() work under the hood?enum.toString() 如何在幕后工作?
【发布时间】:2014-03-25 16:00:46
【问题描述】:

我是一名 iOS 开发人员,我很遗憾在 Objective-C 中没有像 C# 中那样用于枚举的惊人方法 toString

所以我很想知道 toString 方法是如何在 C# 中用于枚举的。

也许有了你的回答,我就会明白为什么 Objective-C 没有实现这个方法。

谢谢

【问题讨论】:

标签: c# enums tostring


【解决方案1】:

C# 编译器实际上将枚举转换为常规的 class 类型,并通过 syntax sugar 和 .NET 框架中方法的一些帮助(特别是 TypeEnum类)。

当您声明 enum 类型时,实际发生的情况是编译器创建了一个隐藏的、特殊命名的类,它在概念上如下所示:

public sealed class EnumName : Enum
{
    public static readonly int FirstValue = 0;
    public static readonly int SecondValue = 1;
    // etc.
}

Enum 基类是一个特殊的基类,不能直接使用。它提供了ToString() 方法。这引发了 .NET 框架内部的一系列函数调用。代码以Type.GetEnumName() 结尾,最后是一个私有Type.GetEnumData() 方法,该方法使用反射检查EnumName 类,查看其字段(枚举值)并获取并返回它们的名称。

调用图(按时间顺序)如下所示:

Enum.ToString()
  Enum.InternalFormat(RuntimeType, object)
    Enum.GetName(RuntimeType, object)
      Type.GetEnumName(object)
        Type.GetEnumRawConstantValues()
          Type.GetEnumData(out string[], out Array)
        Type.GetEnumNames()
          Type.GetEnumData(out string[], out Array)

GetEnumData() 调用Type.GetFields() 并对其进行迭代,将每个字段的名称和整数值(枚举值)复制到输出数组中。

【讨论】:

  • 谢谢,您的回答很清楚,我非常了解 enum toString 方法的工作原理。
【解决方案2】:

ToString 内部会调用一个名为Enum.InternalFormat 的方法,而后者又只是调用Enum.GetName,这是反编译的代码(.NET v4.0)

[__DynamicallyInvokable]
public override string ToString()
{
  return Enum.InternalFormat((RuntimeType) this.GetType(), this.GetValue());
}

private static string InternalFormat(RuntimeType eT, object value)
{
  if (!eT.IsDefined(typeof (FlagsAttribute), false))
    return Enum.GetName((Type) eT, value) ?? value.ToString();
  else
    return Enum.InternalFlagsFormat(eT, value);
}

对于标有Flags 属性的枚举,它会调用另一个名为InternalFlagsFormat 的方法,该方法会构建一个包含所有枚举名称的CSV

private static string InternalFlagsFormat(RuntimeType eT, object value)
{
  ulong num1 = Enum.ToUInt64(value);
  ulong[] values;
  string[] names;
  Enum.GetCachedValuesAndNames(eT, out values, out names, true, true);
  int index = values.Length - 1;
  StringBuilder stringBuilder = new StringBuilder();
  bool flag = true;
  ulong num2 = num1;
  for (; index >= 0 && (index != 0 || (long) values[index] != 0L); --index)
  {
    if (((long) num1 & (long) values[index]) == (long) values[index])
    {
      num1 -= values[index];
      if (!flag)
        stringBuilder.Insert(0, ", ");
      stringBuilder.Insert(0, names[index]);
      flag = false;
    }
  }
  if ((long) num1 != 0L)
    return value.ToString();
  if ((long) num2 != 0L)
    return ((object) stringBuilder).ToString();
  if (values.Length > 0 && (long) values[0] == 0L)
    return names[0];
  else
    return "0";
} 

有关最新实施,请参阅Microsoft source reference

【讨论】:

【解决方案3】:

ToString 只返回与特定枚举值的名称等效的字符串(注意,这可能与它的值不同)。

public enum SomeEnum {
    Text1 = 1,
    Text2 = 2,
    Text3 = 3,
}

var some = SomeEnum.Text1

some.ToString() 返回"Text1"

在标记枚举的情况下(在声明中使用 FlagsAttribute),它将返回一个与所有选定枚举值等效的字符串

var some = SomeEnum.Text1 & SomeEnum.Text2

"Text1 | Text2"

【讨论】:

  • 这不能回答问题。问题不在于ToString 做了什么,而是它是如何做到的。
  • 我相信它正在使用反射来完成这个(反正我会这样做)。
  • 所以是 C# 编译器为枚举的每个键保留一个字符串?
  • 如果我们有一个枚举类型的实例,我们可以通过调用从 System.Enum 继承的 ToString 方法将该值映射到多个字符串表示形式之一
【解决方案4】:

如果我们有一个枚举类型的实例,我们可以通过调用从 System.Enum 继承的 ToString 方法将该值映射到多个字符串表示形式之一

下面是示例代码

class Program
{
    enum Days
    {
        Sun,
        Mon,
        Tue,
        Wed,
        Thu,
        Fri,
        Sat
    };

    static void Main(string[] args)
    {

        Days obj = Days.Mon;

        Console.WriteLine(obj.ToString("G"));  // output - Mon (Normal)
        Console.WriteLine(obj.ToString("D"));  // output - 1 (Decimal)
        Console.WriteLine(obj.ToString("X"));  // output - 00000001 (Hexadecimal)    
    }
}

【讨论】:

    猜你喜欢
    • 2014-11-18
    • 2011-02-17
    • 2012-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-10
    相关资源
    最近更新 更多