2012-04-24 6 views
11

열거 형과 다른 객체 (이 예제에서는 문자열) 사이에 전역 하드 코딩 매핑이 자주 필요합니다. 유지 관리를 명확히하기 위해 열거 형 및 매핑 정의를 함께 배치하고자합니다.enum 클래스를 사용하여 enum-to-object 매핑을 유지 하시겠습니까?

이 예제에서 하나의 정적 필드가있는 짜증나는 클래스가 생성됩니다.

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

나는 'class sFile = EmailTemplate.Confirm.TemplateFile;'과 같은 구문을 사용하여 클래스 사용을 제거하려고한다고 생각한다. – AMissico

+0

Martin! 고맙습니다! 애트리뷰트는 대부분의 상황 (문자열이 적절한 상황)에 적합한 솔루션입니다. 나는 복잡한 객체가 각 열거 형으로 매핑되는 경우를 알고 있지만이 경우에도 소수의 스칼라를 처리 할 수 ​​있습니다. – shannon

+0

더 많이 사용할수록 더 멋지게됩니다. 다시 마틴, 고마워. – shannon

2

일반적으로 열거 형 요소에 추가 정보 또는 동작을 추가하려는 경우에는 대신 전체 클래스가 필요합니다. 당신은 (old-) 자바 타입 안전 열거 패턴에서 빌려와 같은 것을 만들 수 있습니다

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) { ... } 
} 

지금 위 LocationRender처럼, 당신의 요소에 어떤 속성 또는 메서드를 연결할 수 있습니다.

+0

감사합니다. 아주 좋은 전략입니다. 스펙트럼의 다른 쪽 끝에있는 간격을 채 웁니다. – shannon

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

좋은 접근 방식을 사용하면 속성에 대해보다 세부적인 제어가 가능하며 반사> 매핑> 사람이 읽을 수있는 형식의 관점에서 수행 할 수있는 방법이 적습니다. –

관련 문제