2012-04-24 117 views
11

我经常需要枚举和另一个对象(本例中为字符串)之间的全局硬编码映射。我想共同定位枚举和映射定义来阐明维护。保持与枚举类的枚举到对象映射?

正如你所看到的,在这个例子中,创建了一个带有一个静态字段的恼人​​的类。

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"); 
    } 
} 

有时映射类可以有更多的功能和有意义的名称,映射活动甚至可以是私有的。但是这只会污染维护/关联问题。

任何人都有这样的好模式?

回答

6

您可以使用属性来注释枚举,然后使用反射来构建字典。

[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

+0

我想他们想删除类的使用,像'string sFile = EmailTemplate.Confirm.TemplateFile;'语法。 – AMissico 2012-04-25 00:33:44

+0

马丁!谢谢!对于大多数情况(字符串是合适的)来说,属性是一个很好的解决方案。我知道我有一些情况,一个复杂的对象映射到每个枚举,但是这甚至可以覆盖一些标量。 – shannon 2012-04-26 03:34:25

+0

我用得越多越好。再次感谢马丁。 – shannon 2012-04-26 22:02:18

2

通常情况下,当你想向你的枚举元素添加额外的信息或行为时,这意味着你需要一个完整的吹类。您可以从(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

+0

谢谢,也是一个非常好的策略。填补了光谱另一端的空白。 – shannon 2012-05-09 07:00:38

2

这是一种对我来说工作得很好的方法。

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 
} 

现在可以使用反射来枚举映射到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)); 
} 

下面是使用这种映射来枚举映射到对象,然后拉字段断它的一个例子:

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; 
    } 
+0

不错的方法,你可以对属性进行更细粒度的控制,并且在反射>贴图>人类可读形式方面做的更少。 – 2017-05-14 16:31:59