Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does enum.toString() work under the hood?

Tags:

c#

enums

tostring

I am an iOS developer and I regret that in Objective-C there is no the amazing method toString for enum like in C#

So I am very curious to know how the toString method works for enum in C#.

Maybe with your answers I will understand why Objective-C has not this method implemented.

Thanks

like image 986
Jean Lebrument Avatar asked Mar 25 '14 16:03

Jean Lebrument


People also ask

What does enum toString do?

toString() method returns the name of this enum constant, as contained in the declaration.

How do you localize an enum in Java?

To localize the enumeration names and values, add messages with the following keys to the message pack located in the Java package of the enumeration class: Enumeration name key – simple class name (without package); Value key – simple class name, then the value name separated by period.

Does enum have toString?

The toString() method of Enum class returns the name of this enum constant, as the declaration contains. The toString() method can be overridden, although it's not essential.

What is the default toString of an enum?

By default, Enum. toString() returns the same value as Enum.name().


2 Answers

The C# compiler actually turns enumerations into regular class types, and makes them behave like an enumeration by a combination of syntax sugar and some help from methods in the .NET framework (particularly the Type and Enum classes).

What actually happens when you declare an enum type is that the compiler creates a hidden, specially-named class which conceptually looks like this:

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

The Enum base class is a special base class that you cannot use directly. It provides the ToString() method. This sets off a series of function calls internal to the .NET framework. The code ends up in Type.GetEnumName() and finally a private Type.GetEnumData() method which uses reflection to examine the EnumName class, look at its fields (enumeration values) and get and return their names.

The call graph (in chronological order) looks like this:

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() calls Type.GetFields() and iterates over them, copying the name and integral value (enum value) of each field into the output array.

like image 143
TypeIA Avatar answered Oct 13 '22 01:10

TypeIA


Internally ToString will call a method called Enum.InternalFormat, which in turn just calls Enum.GetName, here's the decompiled code (.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);
}

For enums marked with the Flags attribute, it calls another method called InternalFlagsFormat which builds up a CSV of all the enum names

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";
} 

For the latest implementation, see the Microsoft source reference.

like image 23
James Avatar answered Oct 13 '22 03:10

James