// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.Contracts;
using System.Reflection;
namespace System.Web.Mvc.Html
{
public static class EnumHelper
{
///
/// Gets a value indicating whether the given or an expression of this
/// is suitable for use in and
/// calls.
///
/// The to check.
///
/// if will not throw when passed given
/// and
/// will not throw when passed an expression of this ; otherwise.
///
///
/// Currently returns if the parameter is
/// non-, is an type, and does not have a
/// attribute.
///
public static bool IsValidForEnumHelper(Type type)
{
bool isValid = false;
if (type != null)
{
// Type.IsEnum is false for Nullable even if T is an enum. Check underlying type (if any).
// Do not support Enum type itself -- IsEnum property is false for that class.
Type checkedType = Nullable.GetUnderlyingType(type) ?? type;
if (checkedType.IsEnum)
{
isValid = !HasFlagsInternal(checkedType);
}
}
return isValid;
}
///
/// Gets a list of objects corresponding to enum constants defined in the given
/// .
///
/// The to evaluate.
/// An for the given .
///
/// Throws if returns for the given
/// .
///
public static IList GetSelectList(Type type)
{
if (type == null)
{
throw new ArgumentNullException("type");
}
if (!IsValidForEnumHelper(type))
{
throw new ArgumentException("非枚举类型,不支持", "type");
}
IList selectList = new List();
// According to HTML5: "The first child option element of a select element with a required attribute and
// without a multiple attribute, and whose size is "1", must have either an empty value attribute, or must
// have no text content." SelectExtensions.DropDownList[For]() methods often generate a matching
// . Empty value for Nullable, empty text for round-tripping an unrecognized value, or option
// label serves in some cases. But otherwise, ignoring this does not cause problems in either IE or Chrome.
Type checkedType = Nullable.GetUnderlyingType(type) ?? type;
if (checkedType != type)
{
// Underlying type was non-null so handle Nullable; ensure returned list has a spot for null
selectList.Add(new SelectListItem { Text = String.Empty, Value = String.Empty, });
}
// Populate the list
const BindingFlags BindingFlags =
BindingFlags.DeclaredOnly | BindingFlags.GetField | BindingFlags.Public | BindingFlags.Static;
foreach (FieldInfo field in checkedType.GetFields(BindingFlags))
{
// fieldValue will be an numeric type (byte, ...)
object fieldValue = field.GetRawConstantValue();
selectList.Add(new SelectListItem { Text = GetDisplayName(field), Value = fieldValue.ToString(), });
}
return selectList;
}
private static bool HasFlagsInternal(Type type)
{
Contract.Assert(type != null);
FlagsAttribute attribute = type.GetCustomAttribute(inherit: false);
return attribute != null;
}
// Return non-empty name specified in a [Display] attribute for the given field, if any; field's name otherwise
private static string GetDisplayName(FieldInfo field)
{
DisplayAttribute display = field.GetCustomAttribute(inherit: false);
if (display != null)
{
string name = display.GetName();
if (!String.IsNullOrEmpty(name))
{
return name;
}
}
return field.Name;
}
}
}