为什么和如何(内部)Enum.IsDefined同时搜索名称和值?

18

假设我们定义了一个Planets枚举:

public enum Planets
{
    Sun = 0,
    Mercury=5,          
    Venus,              
    Earth,          
    Jupiter,        
    Uranus,         
    Neptune   
}

我曾使用 Enum.IsDefined 方法来确定字符串是否存在于枚举类型中。
Enum.IsDefined(typeof(Planets), "Mercury"); // result is true

但是,之后我尝试了这个方法,它也返回了true:

Enum.IsDefined(typeof(Planets), 5); // result is true again

这个方法怎么实现的?它没有任何重载,只有一个签名:

Enum.IsDefined(Type enumType, object value);

为什么和如何使用 Enum.IsDefined 搜索名称和值?这对我来说非常有意思,他们为什么选择这种方式呢?在我看来,制作重载函数会是更好的选择,不是吗?
2个回答

15

根据Enum.IsDefined方法

value参数可以是以下任意一种

  • enumType类型的任何成员。
  • 值为enumType类型的枚举成员的变量。
  • 枚举成员名称的字符串表示形式。 字符串中的字符必须与枚举成员名称的大小写相同。
  • enumType的基础类型的值。

我认为这就是为什么它没有重载并将object作为第二个参数的原因。由于该方法将object作为第二个参数 - 而object是所有.NET类型的基类 - 您可以传递stringint等。

这里是该方法的实现方式;

public static bool IsDefined(Type enumType, Object value)
{
     if (enumType == null)
         throw new ArgumentNullException("enumType");                    
     return enumType.IsEnumDefined(value);
}

看起来这个虚拟的 Type.IsEnumDefined 方法会处理所有这些情况,就像它的实现一样;

    public virtual bool IsEnumDefined(object value)
    {
        if (value == null)
            throw new ArgumentNullException("value");

        if (!IsEnum)
            throw new ArgumentException(Environment.GetResourceString("Arg_MustBeEnum"), "enumType");
        Contract.EndContractBlock();

        // Check if both of them are of the same type
        Type valueType = value.GetType();

        // If the value is an Enum then we need to extract the underlying value from it
        if (valueType.IsEnum)
        {
            if (!valueType.IsEquivalentTo(this))
                throw new ArgumentException(Environment.GetResourceString("Arg_EnumAndObjectMustBeSameType", valueType.ToString(), this.ToString()));

            valueType = valueType.GetEnumUnderlyingType();
        }

        // If a string is passed in
        if (valueType == typeof(string))
        {
            string[] names = GetEnumNames();
            if (Array.IndexOf(names, value) >= 0)
                return true;
            else
                return false;
        }

        // If an enum or integer value is passed in
        if (Type.IsIntegerType(valueType))
        {
            Type underlyingType = GetEnumUnderlyingType();
            // We cannot compare the types directly because valueType is always a runtime type but underlyingType might not be.
            if (underlyingType.GetTypeCodeImpl() != valueType.GetTypeCodeImpl())
                throw new ArgumentException(Environment.GetResourceString("Arg_EnumUnderlyingTypeAndObjectMustBeSameType", valueType.ToString(), underlyingType.ToString()));

            Array values = GetEnumRawConstantValues();
            return (BinarySearch(values, value) >= 0);
        }
    }

1
是的,谢谢。另外,我很高兴知道http://referencesource.microsoft.com/的存在。对我来说很有趣,他们是否检查输入的类型?从这个方法看来似乎是的。 - Farhad Jabiyev
1
@FarhadJabiyev 非常感谢。是的,正如您在 Type.IsEnumDefined 方法的实现中所看到的那样,它通过使用 if(valueType.IsEnum)if(valueType == typeof(string))if(Type.IsIntegerType(valueType)) 来检查其类型。 - Soner Gönül

2
Enum.IsDefined的文档中,它指出这个值是:

Type:System.Object

枚举类型中常量的值或名称。

你已经给水星赋值为5,因此它能够看到这个值。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接