Как работает enum.toString() работает под капотом?



Я разработчик iOS и сожалею, что в Objective-C нет удивительного метода toString для перечисления, как в C#



Поэтому мне очень любопытно узнать, как метод toString работает для перечисления в C#.



Может быть, с вашими ответами я пойму, почему Objective-C не реализовал этот метод.



Спасибо

672   4  

4 ответов:

Компилятор C# фактически превращает перечисления в обычные типы class и заставляет их вести себя как перечисление комбинацией синтаксического сахара и некоторой помощью методов в .NET framework (в частности,Type и еще Enum классы).

На самом деле, когда вы объявляете Тип enum, компилятор создает скрытый, специально именованный класс, который концептуально выглядит следующим образом:

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

Базовый класс Enum является специальным базовый класс, который нельзя использовать напрямую. Он предоставляет метод ToString(). Это запускает серию вызовов функций, внутренних для платформы .NET framework. Код заканчивается в 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 value) каждого поля в выходном массиве.

Внутренне 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 .

ToString просто возвращает строку, равную имени конкретного значения enum (обратите внимание, что это может отличаться от его значения).

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, унаследованный от System.Перечисление

Ниже приведен пример кода

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

Comments

    Ничего не найдено.