Связывание дополнительной информации с .NET Enum
Мой вопрос лучше всего иллюстрируется примером.
Предположим, что у меня есть перечисление:
public enum ArrowDirection
{
North,
South,
East,
West
}
Я хочу связать единичный вектор, соответствующий каждому направлению с этим направлением. Например, я хочу что-то, что вернет (0, 1) для North, (-1, 0) для West и т.д. Я знаю, что в Java вы можете объявить метод внутри перечисления, который мог бы обеспечить эту функциональность.
Мое текущее решение - иметь статический метод - внутри класса, который определяет перечисление, - который возвращает вектор, соответствующий переданному в ArrowDirection (метод использует HashTable для выполнения поиска, но это не очень важно). Это кажется... нечистым.
Вопрос:
Есть ли лучшее решение для хранения дополнительной информации, соответствующей перечислению в .NET?
Ответы
Ответ 1
В С# 3.0 появился новый способ FANTASTIC. Ключом является этот красивый факт: у Enums могут быть методы расширения! Итак, вот что вы можете сделать:
public enum ArrowDirection
{
North,
South,
East,
West
}
public static class ArrowDirectionExtensions
{
public static UnitVector UnitVector(this ArrowDirection self)
{
// Replace this with a dictionary or whatever you want ... you get the idea
switch(self)
{
case ArrowDirection.North:
return new UnitVector(0, 1);
case ArrowDirection.South:
return new UnitVector(0, -1);
case ArrowDirection.East:
return new UnitVector(1, 0);
case ArrowDirection.West:
return new UnitVector(-1, 0);
default:
return null;
}
}
}
Теперь вы можете сделать это:
var unitVector = ArrowDirection.North.UnitVector();
Сладкое! Я только узнал об этом месяц назад, но это очень хорошее следствие новых возможностей С# 3.0.
Вот еще один пример моего блога.
Ответ 2
Я писал об этом здесь.
Попробуйте что-то подобное с помощью Атрибуты.
public enum Status {
[Status(Description = "Not Available")]
Not_Available = 1,
[Status(Description = "Available For Game")]
Available_For_Game = 2,
[Status(Description = "Available For Discussion")]
Available_For_Discussion = 3,
}
public class StatusEnumInfo {
private static StatusAttribute[] edesc;
public static String GetDescription(object e)
{
System.Reflection.FieldInfo f = e.GetType().GetField(e.ToString());
StatusEnumInfo.edesc = f.GetCustomAttributes(typeof(StatusAttribute), false) as StatusAttribute[];
if (StatusEnumInfo.edesc != null && StatusEnumInfo.edesc.Length == 1)
return StatusEnumInfo.edesc[0].Description;
else
return String.Empty;
}
public static object GetEnumFromDesc(Type t, string desc)
{
Array x = Enum.GetValues(t);
foreach (object o in x) {
if (GetDescription(o).Equals(desc)) {
return o;
}
} return String.Empty;
}
}
public class StatusAttribute : Attribute {
public String Description { get; set; }
}
public class Implemenation {
public void Run()
{
Status statusEnum = (Status)StatusEnumInfo.GetEnumFromDesc(typeof(Status), "Not Available");
String statusString = StatusEnumInfo.GetDescription(Status.Available_For_Discussion);
}
}
Вместо описания используйте свое пользовательское свойство
Ответ 3
using System.ComponentModel;
using System.Reflection;
public enum ArrowDirection
{
[Description("Northwards")]
North,
[Description("Southwards")]
South,
[Description("Eastwards")]
East,
[Description("Westwards")]
West
}
...
Создайте метод расширения, чтобы получить список описаний:
public static class Enum<T> where T : struct
{
/// <summary>
/// Gets a collection of the enum value descriptions.
/// </summary>
/// <returns></returns>
public static IList<string> GetDescriptions()
{
List<string> descriptions = new List<string>();
foreach (object enumValue in Enum<T>.GetValues())
{
descriptions.Add(((Enum)enumValue).ToDescription());
}
return descriptions;
}
}
Ответ 4
Одна вещь, на которую вы могли бы обратить внимание, - это шаблон "Тип-безопасный переход". Это позволяет создать перечисление, которое на самом деле является полноценным статическим объектом, который может иметь методы/свойства/и т.д.
http://www.javacamp.org/designPattern/enum.html
Джошуа Блох рассказывает об этом шаблоне в своей книге "Эффективная Ява". Я использовал его во многих разных ситуациях, и я предпочитаю его на простых перечислениях. (Это язык-агностик - он работает на Java, С# или почти любом языке OO).
Ответ 5
Ваш подход к статическому методу кажется мне совершенно чистым. Вы инкапсулируете как перечисление, так и статический метод в один класс. Изменения в перечислении централизованы внутри этого единственного класса.
Добавление метода к перечислению (в соответствии с Java), похоже, добавляет сложности к чему-то, что действительно очень простое понятие.
Подход, основанный на атрибутах, интересен, но в очередной раз кажется, что он перегружает вещи по сравнению со статическим методом.