Метод расширения для перечисления, а не экземпляр перечисления
У меня есть перечисление для моих вещей вроде:
public enum Things
{
OneThing,
AnotherThing
}
Я хотел бы написать метод расширения для этой перечисления (аналогичный здесь, здесь), но пока этот метод работает над экземпляром перечисления, ala
Things thing; var list = thing.ToSelectList();
Я хотел бы, чтобы он работал над фактическим перечислением:
var list = Things.ToSelectList();
Я мог просто сделать
var list = default(Things).ToSelectList();
Но мне это не нравится.
Я подошел ближе к следующему методу расширения:
public static SelectList ToSelectList(this Type type)
{
if (type.IsEnum)
{
var values = from Enum e in Enum.GetValues(type)
select new { ID = e, Name = e.ToString() };
return new SelectList(values, "Id", "Name");
}
else
{
return null;
}
}
Используется так:
var list = typeof(Things).ToSelectList();
Можем ли мы сделать что-то лучше этого?
Ответы
Ответ 1
Методы расширения работают только с экземплярами, поэтому это невозможно, но с некоторыми хорошо подобранными именами и обобщенными именами классов и методов вы можете получить результат, который выглядит так же хорошо:
public class SelectList
{
// Normal SelectList properties/methods go here
public static SelectList Of<T>()
{
Type t = typeof(T);
if (t.IsEnum)
{
var values = from Enum e in Enum.GetValues(type)
select new { ID = e, Name = e.ToString() };
return new SelectList(values, "Id", "Name");
}
return null;
}
}
Затем вы можете получить список выбора следующим образом:
var list = SelectList.Of<Things>();
IMO это читается намного лучше, чем Things.ToSelectList()
.
Ответ 2
Нет.
Лучшее, что вы можете сделать, это поставить его на статический класс, например:
public static class ThingsUtils {
public static SelectList ToSelectList() { ... }
}
Ответ 3
Ответ на Aaronaught действительно замечательный, на основе которого я сделал следующую реализацию:
public class SelectList
{
public static IEnumerable<Enum> Of<T>() where T : struct, IConvertible
{
Type t = typeof(T);
if (t.IsEnum)
{
return Enum.GetValues(t).Cast<Enum>();
}
throw new ArgumentException("<T> must be an enumerated type.");
}
}
По-моему, это немного безопаснее, поскольку вы можете - почти - называть его только с помощью Enums, и, конечно, вместо броска вы можете просто вернуть null, если хотите версию без исключений.
Ответ 4
@Aaronaught имеет очень хороший ответ. Чтобы продлить его ответ, вы можете даже сделать его более общим. У меня это в глобальной библиотеке...
public static IQueryable GetAllEnumValues<T>()
{
IQueryable retVal = null;
Type targetType = typeof(T);
if(targetType.IsEnum)
{
retVal = Enum.GetValues(targetType).AsQueryable();
}
return retVal;
}
Теперь вы отключили эту функциональность от класса SelectList. Таким образом, вы можете вызвать это в методах SelectList или в любом другом месте.
public class SelectList
{
public static SelectList Of<T>
{
IQueryable enumValues = GetAllEnumValues<T>();
var values =
from Enum e in enumValues
select new { ID = e, Name = e.ToString() };
return new SelectList(values, "Id", "Name");
}
}
Ответ 5
Я использую 'Type' вместо 'Enum' для добавления расширения. Затем я могу получить любой тип списка обратно из метода. Здесь он возвращает строковые значения:
public static string[] AllDescription(this Type enumType)
{
if (!enumType.IsEnum) return null;
var list = new List<string>();
var values = Enum.GetValues(enumType);
foreach (var item in values)
{
// add any combination of information to list here:
list.Add(string.Format("{0}", item));
//this one gets the values from the [Description] Attribute that I usually use to fill drop downs
//list.Add(((Enum) item).GetDescription());
}
return list.ToArray();
}
Позже я мог бы использовать этот синтаксис для получения того, что хочу:
var listOfThings = typeof (Things).AllDescription();
Ответ 6
По-моему, это самый чистый путь. Почему?
- Он работает для любого
System.Enum
- Сам метод расширения является более чистым.
- Чтобы вызвать его, просто добавьте
new
и это небольшой компромисс (потому что он должен иметь экземпляр для работы.
- Вы не проходите
null
вокруг, и он буквально не будет компилироваться, если вы попытаетесь использовать его с другим типом.
Использование:
(new Things()).ToSelectList()
Метод расширения:
[Extension()]
public SelectList ToSelectList(System.Enum source)
{
var values = from Enum e in Enum.GetValues(source.GetType)
select new { ID = e, Name = e.ToString() };
return new SelectList(values, "Id", "Name");
}
Ответ 7
Ближе всего вы можете прийти, я думаю, к манекенам немного, чтобы работать как переименование, не будучи одним. Вот то, что я придумал - кажется, что много работы просто для того, чтобы использовать статический метод для перечисления, хотя я понимаю его программный подход:
public class PseudoEnum
{
public const int FirstValue = 1;
private static PseudoEnum FirstValueObject = new PseudoEnum(1);
public const int SecondValue = 2;
private static PseudoEnum SecondValueObject = new PseudoEnum(2);
private int intValue;
// This prevents instantation; note that we cannot mark the class static
private PseudoEnum() {}
private PseudoEnum(int _intValue)
{
intValue = _intValue;
}
public static implicit operator int(PseudoEnum i)
{
return i.intValue;
}
public static implicit operator PseudoEnum(int i)
{
switch (i)
{
case FirstValue :
return FirstValueObject;
case SecondValue :
return SecondValueObject;
default:
throw new InvalidCastException();
}
}
public static void DoSomething(PseudoEnum pe)
{
switch (pe)
{
case PseudoEnum.FirstValue:
break;
case PseudoEnum.SecondValue:
break;
}
}
}