2012-04-24 184 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