【发布时间】:2014-03-25 16:00:46
【问题描述】:
我是一名 iOS 开发人员,我很遗憾在 Objective-C 中没有像 C# 中那样用于枚举的惊人方法 toString
所以我很想知道 toString 方法是如何在 C# 中用于枚举的。
也许有了你的回答,我就会明白为什么 Objective-C 没有实现这个方法。
谢谢
【问题讨论】:
-
使用 Telerik JustDecompile 之类的东西,自己看看。
我是一名 iOS 开发人员,我很遗憾在 Objective-C 中没有像 C# 中那样用于枚举的惊人方法 toString
所以我很想知道 toString 方法是如何在 C# 中用于枚举的。
也许有了你的回答,我就会明白为什么 Objective-C 没有实现这个方法。
谢谢
【问题讨论】:
C# 编译器实际上将枚举转换为常规的 class 类型,并通过 syntax sugar 和 .NET 框架中方法的一些帮助(特别是 Type 和 Enum类)。
当您声明 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() 并对其进行迭代,将每个字段的名称和整数值(枚举值)复制到输出数组中。
【讨论】:
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 只返回与特定枚举值的名称等效的字符串(注意,这可能与它的值不同)。
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.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)
}
}
【讨论】: