Сохранять отображение перечисления в объект с классом enum?
Мне часто требуется глобальное жестко закодированное сопоставление между enum и другим объектом (строка в этом примере). Я хочу совместить определения перечисления и отображения для уточнения обслуживания.
Как вы можете видеть, в этом примере создается раздражающий класс с одним статическим полем.
public enum EmailTemplates
{
// Remember to edit the corresponding mapping singleton!
WelcomeEmail,
ConfirmEmail
}
public class KnownTemplates
{
public static Dictionary<EmailTemplates, string> KnownTemplates;
static KnownTemplates() {
KnownTemplates.Add(EmailTemplates.WelcomeEmail, "File1.htm");
KnownTemplates.Add(EmailTemplates.ConfirmEmail, "File2.htm");
}
}
Иногда класс сопоставления может иметь больше функций и значимое имя, а активность отображения может быть даже частной. Но это только загрязняет проблему поддержания/корреляции.
У кого-нибудь есть хороший шаблон для этого?
Ответы
Ответ 1
Вы можете использовать атрибуты для аннотирования перечисления, а затем использовать отражение для создания словаря.
[AttributeUsage(AttributeTargets.Field)]
sealed class TemplateAttribute : Attribute {
public TemplateAttribute(String fileName) {
FileName = fileName;
}
public String FileName { get; set; }
}
enum EmailTemplate {
[Template("File1.htm")]
WelcomeEmail,
[Template("File2.htm")]
ConfirmEmail
}
class KnownTemplates {
static Dictionary<EmailTemplate, String> knownTemplates;
static KnownTemplates() {
knownTemplates = typeof(EmailTemplates)
.GetFields(BindingFlags.Static | BindingFlags.Public)
.Where(fieldInfo => Attribute.IsDefined(fieldInfo, typeof(TemplateAttribute)))
.Select(
fieldInfo => new {
Value = (EmailTemplate) fieldInfo.GetValue(null),
Template = (TemplateAttribute) Attribute
.GetCustomAttribute(fieldInfo, typeof(TemplateAttribute))
}
)
.ToDictionary(x => x.Value, x => x.Template.FileName);
}
}
Если вы сделаете это много, вы можете создать более общую общую функцию, которая объединяет значения перечисления с атрибутом, связанным с этим значением перечисления:
static IEnumerable<Tuple<TEnum, TAttribute>> GetEnumAttributes<TEnum, TAttribute>()
where TEnum : struct
where TAttribute : Attribute {
return typeof(TEnum)
.GetFields(BindingFlags.Static | BindingFlags.Public)
.Where(fieldInfo => Attribute.IsDefined(fieldInfo, typeof(TAttribute)))
.Select(
fieldInfo => Tuple.Create(
(TEnum) fieldInfo.GetValue(null),
(TAttribute) Attribute.GetCustomAttribute(fieldInfo, typeof(TAttribute))
)
);
}
И используйте его следующим образом:
knownTemplates = GetEnumAttributes<EmailTemplate, TemplateAttribute>()
.ToDictionary(tuple => tuple.Item1, tuple => tuple.Item2.FileName);
Для еще большего удовольствия вы можете создать метод расширения:
static class EmailTemplateExtensions {
static Dictionary<EmailTemplate, String> templates;
static EmailTemplateExtensions() {
templates = GetEnumAttributes<EmailTemplate, TemplateAttribute>()
.ToDictionary(tuple => tuple.Item1, tuple => tuple.Item2.FileName);
}
public static String FileName(this EmailTemplate emailTemplate) {
String fileName;
if (templates.TryGetValue(emailTemplate, out fileName))
return fileName;
throw new ArgumentException(
String.Format("No template defined for EmailTemplate.{0}.", emailTemplate)
);
}
}
Затем вызов EmailTemplate.ConfirmEmail.FileName()
вернет File2.htm
.
Ответ 2
Обычно, когда вы хотите добавить дополнительную информацию или поведение к элементам enum, это означает, что вам нужен полный раздутый класс. Вы можете заимствовать у (old-) Java типизированный шаблон перечисления и создать что-то вроде этого:
sealed class EmailTemplate {
public static readonly EmailTemplate Welcome = new EmailTemplate("File1.html");
public static readonly EmailTemplate Confirm = new EmailTemplate("File2.html");
private EmailTemplate(string location) {
Location = location;
}
public string Location { get; private set; }
public string Render(Model data) { ... }
}
Теперь вы можете связать любые свойства или методы с вашими элементами, например Location
и Render
.
Ответ 3
Вот подход, который очень хорошо работал у меня.
public class BaseErrWarn : Attribute
{
public string Code { get; set; }
public string Description { get; set; }
public BaseErrWarn(string code, string description)
{
this.Code = code;
this.Description = description;
}
}
public enum ErrorCode
{
[BaseErrWarn("ClientErrMissingOrEmptyField", "Field was missing or empty.")] ClientErrMissingOrEmptyField,
[BaseErrWarn("ClientErrInvalidFieldValue", "Not a valid field value.")] ClientErrInvalidFieldValue,
[BaseErrWarn("ClientErrMissingValue", "No value passed in.")] ClientErrMissingValue
}
Теперь вы можете использовать отражение для отображения Enum в класс BaseErrWarn:
public static BaseErrWarn GetAttribute(Enum enumVal)
{
return (BaseErrWarn)Attribute.GetCustomAttribute(ForValue(enumVal), typeof(BaseErrWarn));
}
private static MemberInfo ForValue(Enum errorEnum)
{
return typeof(BaseErrWarn).GetField(Enum.GetName(typeof(BaseErrWarn), errorEnum));
}
Вот пример, который использует это сопоставление для отображения Enum для объекта, а затем вычеркивает поля:
public BaseError(Enum errorCode)
{
BaseErrWarn baseError = GetAttribute(errorCode);
this.Code = baseError.Code;
this.Description = baseError.Description;
}
public BaseError(Enum errorCode, string fieldName)
{
BaseErrWarn baseError = GetAttribute(errorCode);
this.Code = baseError.Code;
this.Description = baseError.Description;
this.FieldName = fieldName;
}