Перечисления .NET символьных констант для строковых значений - PullRequest
3 голосов
/ 01 июня 2009

У меня есть список довольно бессмысленных кодов, которые я обрабатываю с помощью приложения VB.NET для Windows. Для бизнес-логики, которую я пишу для обработки этих кодов, я хотел бы использовать значимые константы (например, ServiceNotCovered или MemberNotEligible) вместо исходных кодов (например, "SNCV" и "MNEL").

Насколько я могу судить, Enums может отображать только числовые значения, а не строки. Поэтому лучшее, что я смог придумать, - это статический класс, который представляет константы как статические строковые поля только для чтения, которые внутренне установлены равными значениям кода, как показано ниже.

Public Class MyClass

    private _reasonCode as String()
    Public Property ReasonCode() As String
        'Getter and Setter...
    End Property

    Public Class ReasonCodeEnum
        Private Sub New()
        End Sub
        Public Shared ReadOnly ServiceNotCovered As String = "SNCV"
        Public Shared ReadOnly MemberNotEligible As String = "MNEL"
        'And so forth...
    End Class

End Class

'Calling method
Public Sub ProcessInput()
    Dim obj As New MyClass()
    Select Case obj.ReasonCode
        Case MyClass.ReasonCodeEnum.ServiceNotCovered
            'Do one thing
        Case MyClass.ReasonCodeEnum.MemberNotEligible
            'Do something different
        'Other enum value cases and default
    End Select
End Sub

В приведенном выше примере было бы неплохо, если бы я мог определить MyClass.ReasonCode как имеющий тип ReasonCodeEnum, но тогда мне пришлось бы сделать ReasonCodeEnum нестатическим классом и дать ему способ установки и возврата значение.

Что меня интересует, так это то, есть ли способ использовать встроенную функциональность Enum для того, чтобы делать то, что я делаю, или, если нет, существуют ли какие-либо стандартные шаблоны проектирования для такого типа вещей.

Ответы [ 7 ]

7 голосов
/ 01 июня 2009

Вы можете поместить строки в словарь и найти эквивалентное значение enum вместо большого оператора Select Case:

Public Enum ReasonCode
    ServiceNotCovered
    MemberNotEligible
End Enum


Private mapping As New Dictionary(Of String, ReasonCode)
' Add the required mappings and look up the dictionary...
2 голосов
/ 01 июня 2009

Два варианта предлагают себя:

1) Используйте перечисление и добавьте атрибут Description к каждому значению. Затем вы можете довольно легко построить карту от значения до описания.

Преимущества:

  • Все еще тип значения
  • Можно использовать в операторах switch

Недостатки:

  • Не совсем ОО

2) Не используйте перечисление .NET - используйте что-то более похожее на перечисления Java. Это в основном включает в себя написание открытого неизменяемого класса с частным конструктором и предоставление общедоступных (только для чтения) общих свойств / полей. Вот немного C #, чтобы продемонстрировать, что я имею в виду - надеюсь, вы лучше читаете C #, чем я пишу VB:)

public sealed class Reason
{
    public static readonly Reason ServiceNotCovered = new Reason("SNCV");
    public static readonly Reason MemberNotEligible = new Reason("MNEL");

    private readonly string code;

    private Reason(string code)
    {
        this.code = code;
    }

    public string Code
    {
        get { return code; }
    }
}

Теперь, к сожалению, вы не можете включить это (по крайней мере, не в C # - я не знаю, является ли VB Select более гибким), но куда бы вы не включили 1023 *, стоит подумать о том, сможете ли вы обеспечить ту же функциональность внутри самого перечисления. Это хорошо ОО способ мышления об этом. Различные причины могут обеспечить различную функциональность посредством полиморфизма. Например:

public class Reason
{
    public static readonly Reason ServiceNotCovered = new ServiceReason();
    public static readonly Reason MemberNotEligible = new EligibilityReason();

    private readonly string code;

    private Reason(string code)
    {
        this.code = code;
    }

    public string Code
    {
        get { return code; }
    }

    public abstract void DoSomething();

    private class ServiceReason : Reason
    {
        internal ServiceReason() : base("SVNC") {}

        public override void DoSomething()
        {
            // Whatever
        }
    }

    private class EligibiltyReason : Reason
    {
        internal EligibiltyReason() : base("MNEL") {}

        public override void DoSomething()
        {
            // Do something else
        }
    }
}

Затем вы можете "сгруппировать" разные причины, которые имеют сходное поведение, создав столько производных типов, сколько есть групп - и вызывающему не нужно ничего о них знать.

Все это предполагает, что управление доступом VB работает так же, как и в C #, с точки зрения того, что вложенные типы могут обращаться к закрытым членам (в частности, к конструктору) своих внешних классов.

Это довольно многословное решение с точки зрения кода, но оно помогает сохранить весь процесс принятия решений относительно «enum» прямо в самом типе. Однако есть и другой недостаток, связанный с типом ссылки - поэтому вам нужно будет проверять на ничтожность обычным способом. С другой стороны, перечисления также не обеспечивают реальной защиты от неправильных значений - вы должны использовать Enum.IsDefined, если хотите проверить аргумент.

1 голос
/ 03 июля 2009

Как учебный проект. Оригинальный вопрос и ответ группы превосходны. Кроме того, чтобы иметь конкретную причину чрезмерного усложнения проблемы, почему бы просто не использовать константы?

Const ServiceNotCovered As String = "SNCV"
Const MemberNotEligible As String = "MNEL"

Приведенный выше код является простой статической реализацией. Если изменение или добавление новых значений желательно при фактическом использовании, и / или вам не нужно перекомпилировать, тогда установка значений из внешнего источника данных является еще одной простой альтернативой.

Другая альтернатива - просто установить строковые значения, которые вы можете изменить по желанию. Будь то простые строки, массив, словарь или десятки других методов - основная концепция здесь заключается в использовании простых описательных слов В КОДЕ для справки программистов - пользователь полностью (и должен) не знать об этом соглашении о кодировании.

Так что это действительно выбор программистов, и он ограничен только такими факторами, как повторное использование, частота изменений, понимание другими и уровень ожидаемой изменчивости значений.

1 голос
/ 02 июня 2009

Вы можете эмулировать произвольные перечисления любого типа в VB.Net используя код, который я разместил здесь:
Получение значений статического поля типа с использованием отражения

1 голос
/ 01 июня 2009

Хотя это не решает проблему присвоения строковых значений для enum, что, я думаю, могло бы быть решено, как другие указали с помощью простого словаря

Dictionary<string, MyEnum> StringEnumMap;

Вам следует взглянуть на проект без сохранения состояния в коде Google (мой любимый) или на Simple State Machine в кодовом комплексе для инкапсуляции логики. Его многословие просто удивительно, и я думаю, что оно может идеально соответствовать тому, что вам нужно. Пример с домашней страницы проекта без сохранения состояния:

var phoneCall = new StateMachine<State, Trigger>(State.OffHook);

phoneCall.Configure(State.OffHook)
    .Allow(Trigger.CallDialed, State.Ringing);

phoneCall.Configure(State.Ringing)
    .Allow(Trigger.HungUp, State.OffHook)
    .Allow(Trigger.CallConnected, State.Connected);

phoneCall.Configure(State.Connected)
    .OnEntry(t => StartCallTimer())
    .OnExit(t => StopCallTimer())
    .Allow(Trigger.LeftMessage, State.OffHook)
    .Allow(Trigger.HungUp, State.OffHook)
    .Allow(Trigger.PlacedOnHold, State.OnHold);

phoneCall.Configure(State.OnHold)
    .SubstateOf(State.Connected)
    .Allow(Trigger.TakenOffHold, State.Connected)
    .Allow(Trigger.HungUp, State.OffHook)
    .Allow(Trigger.PhoneHurledAgainstWall, State.PhoneDestroyed);
0 голосов
/ 01 июня 2009

Вы можете иметь два перечисления, одно с загадочными константами, например "NA" и один с описательными константами, например, "Недоступен". Константы, означающие одно и то же, должны отображаться в одно и то же целое число. Преобразовать между перечислениями и целыми числами легко, поэтому остаётся вопрос, как легко преобразовать перечисления в строки

Вот как:

using System.ComponentModel;
...
EnumConverter conv = new EnumConverter( typeof( MyEnum ) );
...
conv.ConvertToString( ... );
conv.ConvertFromString( ... );

Нет гарантий, что это будет работать быстро, но это избавит вас от большого оператора switch.

0 голосов
/ 01 июня 2009

Создать общий словарь, где ключом будет строка (или Enum) и значение в качестве делегата. вызывая словарь по ключу, вы можете выполнить связанное с ним действие.

...